bitkeeper revision 1.1041.5.7 (40e9808fAoDayBeZwAauQ7CRlcKCog)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Mon, 5 Jul 2004 16:23:43 +0000 (16:23 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Mon, 5 Jul 2004 16:23:43 +0000 (16:23 +0000)
Start of code to talk to migration daemon to do migrate.
Initial checkin of the migration daemon, xfrd.

30 files changed:
.rootkeys
tools/libxutil/string_stream.c
tools/python/xen/xend/XendMigrate.py
tools/python/xen/xend/packing.py [new file with mode: 0644]
tools/xfrd/Make.xfrd [new file with mode: 0644]
tools/xfrd/Makefile [new file with mode: 0644]
tools/xfrd/connection.c [new file with mode: 0644]
tools/xfrd/connection.h [new file with mode: 0644]
tools/xfrd/debug.h [new file with mode: 0644]
tools/xfrd/enum.c [new file with mode: 0644]
tools/xfrd/enum.h [new file with mode: 0644]
tools/xfrd/hash_table.c [new file with mode: 0644]
tools/xfrd/hash_table.h [new file with mode: 0644]
tools/xfrd/lexis.c [new file with mode: 0644]
tools/xfrd/lexis.h [new file with mode: 0644]
tools/xfrd/lzi_stream.c [new file with mode: 0644]
tools/xfrd/lzi_stream.h [new file with mode: 0644]
tools/xfrd/marshal.c [new file with mode: 0644]
tools/xfrd/marshal.h [new file with mode: 0644]
tools/xfrd/select.c [new file with mode: 0644]
tools/xfrd/select.h [new file with mode: 0644]
tools/xfrd/sxpr.c [new file with mode: 0644]
tools/xfrd/sxpr.h [new file with mode: 0644]
tools/xfrd/xdr.c [new file with mode: 0644]
tools/xfrd/xdr.h [new file with mode: 0644]
tools/xfrd/xen_domain.c [new file with mode: 0644]
tools/xfrd/xen_domain.h [new file with mode: 0644]
tools/xfrd/xfrd.c [new file with mode: 0644]
tools/xfrd/xfrd.h [new file with mode: 0644]
tools/xfrd/xfrdClient.py [new file with mode: 0755]

index ef35788131abea4ac2575d067639c94e7797f6fc..88e79ce75da44036ed1399adf08355b977469314 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/python/xen/xend/XendVnet.py
 40c9c468x191zetrVlMnExfsQWHxIQ tools/python/xen/xend/__init__.py
 40c9c468S2YnCEKmk4ey8XQIST7INg tools/python/xen/xend/encode.py
+40e9808elkoRulOo1GxRTp5ulJGVNw tools/python/xen/xend/packing.py
 40c9c468DCpMe542varOolW1Xc68ew tools/python/xen/xend/server/SrvBase.py
 40c9c468IxQabrKJSWs0aEjl-27mRQ tools/python/xen/xend/server/SrvConsole.py
 40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/python/xen/xend/server/SrvConsoleDir.py
 403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
 403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format
 4050c413NtuyIq5lsYJV4P7KIjujXw tools/xentrace/xentrace_format.1
+40e9808eHO3QprCFKg9l2JJzgt2voA tools/xfrd/Make.xfrd
+40e9808epTR4zWrYjGUnaaynK20Q5A tools/xfrd/Makefile
+40e9808eysqT4VNDlJFqsZB2rdg4Qw tools/xfrd/connection.c
+40e9808eyXfJUi4E0C3WSgrEXqQ1sQ tools/xfrd/connection.h
+40e9808eULGwffNOE4kBrAfZ9YAVMA tools/xfrd/debug.h
+40e9808eyjiahG5uF6AMelNVujBzCg tools/xfrd/enum.c
+40e9808eZpbdn9q2KSSMGCNvY_ZgpQ tools/xfrd/enum.h
+40e9808easXCzzAZQodEfKAhgUXSPA tools/xfrd/hash_table.c
+40e9808e94BNXIVVKBFHC3rnkvwtJg tools/xfrd/hash_table.h
+40e9808epW9iHcLXuO3QfUfLzB7onw tools/xfrd/lexis.c
+40e9808egccMhCizayQRGtpBA3L5MQ tools/xfrd/lexis.h
+40e9808ePADCSKL1YgGCt2TbYPnYkw tools/xfrd/lzi_stream.c
+40e9808eDNAdpF71o5teYb9DTT-PRw tools/xfrd/lzi_stream.h
+40e9808eQxi0EzTcPJtosrzxEIjA-Q tools/xfrd/marshal.c
+40e9808etg13xfRm0Lqd8vY-jHOoTg tools/xfrd/marshal.h
+40e9808eCsmywryb036TdtRMJHDMmQ tools/xfrd/select.c
+40e9808e99OcM547cKMTfmCVSoWVAw tools/xfrd/select.h
+40e9808e5_PLdodqVOSx0b4T_f5aeg tools/xfrd/sxpr.c
+40e9808e0O4sHZtkDv5hlSqjYcdQAQ tools/xfrd/sxpr.h
+40e9808eF3NVldqRNS5IHM8gbFAvpw tools/xfrd/xdr.c
+40e9808ezXzoRHm7pybXU69NtnjimA tools/xfrd/xdr.h
+40e9808edpUtf4bJ8IbqClPJj_OvbA tools/xfrd/xen_domain.c
+40e9808eHviFFIwdUKOA234uIeifjA tools/xfrd/xen_domain.h
+40e9808eIFeV-MDCNyVTNt5NfMPKeQ tools/xfrd/xfrd.c
+40e9808eGIbOoSNJRiwWK2C3mjGWaA tools/xfrd/xfrd.h
+40e9808eHXvs_5eggj9McD_J90mhNw tools/xfrd/xfrdClient.py
 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING
 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile
 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk
index c3cf423d8463f8b5ccba8a26cdbab25456a01814..495238d74a4799d34fed8e70ba6fb750664e8bb4 100644 (file)
@@ -25,8 +25,6 @@
 #include "string_stream.h"
 #include "allocate.h"
 
-static int string_print(IOStream *io, const char *msg, va_list args);
-static int string_getc(IOStream *io);
 static int string_error(IOStream *io);
 static int string_close(IOStream *io);
 static void string_free(IOStream *io);
@@ -49,45 +47,6 @@ static inline StringData *get_string_data(IOStream *io){
     return (StringData*)io->data;
 }
 
-/** Read a character from a string stream.
- *
- * @param io string stream
- * @return character read, IOSTREAM_EOF if no more input
- */
-static int string_getc(IOStream *io){
-    StringData *data = get_string_data(io);
-    int c = IOSTREAM_EOF;
-    char *s = data->in;
-
-    if(s && s < data->end){
-        c = (unsigned)*s;
-        data->in = s+1;
-    }
-    return c;
-}
-
-/** Print to a string stream.
- * Formats the data to an internal buffer and prints it.
- * The formatted data must fit into the internal buffer.
- *
- * @param io string stream
- * @param format print format
- * @param args print arguments
- * @return result of the print
- */
-static int string_print(IOStream *io, const char *msg, va_list args){
-    StringData *data = get_string_data(io);
-    int k = data->end - data->out;
-    int n = vsnprintf(data->out, k, (char*)msg, args);
-    if(n < 0 || n > k ){
-        n = k;
-        IOStream_close(io);
-    } else {
-        data->out += n;
-    }
-    return n;
-}
-
 /** Test if a string stream has an error.
  *
  * @param io string stream
@@ -118,7 +77,7 @@ static int string_close(IOStream *io){
  */
 static void string_free(IOStream *io){
     StringData *data = get_string_data(io);
-    zero(data, sizeof(*data));
+    memzero(data, sizeof(*data));
     deallocate(data);
 }
 
@@ -139,13 +98,13 @@ IOMethods *string_stream_get_methods(void){
  */
 void string_stream_init(IOStream *io, StringData *data, char *s, int n){
     if(data && io){
-        zero(data, sizeof(*data));
+        memzero(data, sizeof(*data));
         data->string = (char*)s;
         data->in = data->string;
         data->out = data->string;
         data->size = n;
         data->end = data->string + n;
-        zero(io, sizeof(*io));
+        memzero(io, sizeof(*io));
         io->methods = &string_methods;
         io->data = data;
     }
index 1580ba83ed7508891ddfea6640a45fa2a2e7fcf9..471ea7d1ddf9e431b3caf708fb66451ba1be9a46 100644 (file)
 
 import sys
 import socket
+import time
+
+from twisted.internet import reactor
+from twisted.internet import defer
+from twisted.internet.protocol import Protocol
+from twisted.internet.protocol import ClientFactory
 
 import sxp
 import XendDB
 import EventServer; eserver = EventServer.instance()
 
+from xen.xend.packing import SxpPacker, SxpUnpacker
+from xen.xend import XendDomain
+xd = XendDomain.instance()
+
+
+XFRD_PORT = 8002
+
+XFR_PROTO_MAJOR = 1
+XFR_PROTO_MINOR = 0
+
+class Migrate(Protocol):
+
+    def __init__(self, minfo):
+        self.packer = None
+        self.unpacker = None
+        self.minfo = minfo
+
+    def connectionMade(self):
+        self.packer = SxpPacker(self.transport)
+        self.unpacker = SxpPacker()
+        # Send hello.
+        self.packer.pack(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR])
+        # Send migrate.
+        vmconfig = self.minfo.vmconfig()
+        if not vmconfig:
+            self.loseConnection()
+            return
+        self.packer.pack(['xfr.migrate',
+                          self.minfo.src_dom,
+                          vmconfig,
+                          self.minfo.dst_host,
+                          self.minfo.dst_port])
+
+    def connectionLost(self, reason):
+        self.minfo.closed(reason)
+
+    def dataReceived(self, data):
+        try:
+            self.unpacker.reset(data)
+            val = self.unpacker.unpack()
+            print 'dataReceived>', 'val=', val
+            op = val[0]
+            op.replace('.', '_')
+            if op.startwith('xfr_'):
+                fn = getattr(self, op, self.unknown)
+            else:
+                fn = self.unknown
+            fn(val)
+        except Exception, ex:
+            print 'dataReceived>', ex
+            pass
+
+    def unknown(self, val):
+        print 'unknown>', val
+
+    def xfr_progress(self, val):
+        print 'xfr_progress>', val
+
+    def xfr_error(self, val):
+        # If we get an error with non-zero code the migrate failed.
+        # An error with code zero indicates hello success.
+        err = int(val[1])
+        if not err: return
+        self.minfo.error(err);
+        self.loseConnection()
+
+    def xfr_ok(self, val):
+        # An ok indicates migrate completed successfully, and contains
+        # the new domain id on the remote system.
+        dom = int(val[1])
+        self.minfo.ok(dom)
+        self.loseConnection()
+
+class MigrateClientFactory(ClientFactory):
+
+    def __init__(self, minfo):
+        ClientFactory.__init__(self)
+        self.minfo = minfo
+
+    def startedConnecting(self, connector):
+        print 'Started to connect', 'self=', self, 'connector=', connector
+
+    def buildProtocol(self, addr):
+        print 'buildProtocol>', addr
+        return Migrate(self.minfo)
+
+    def clientConnectionLost(self, connector, reason):
+        print 'clientConnectionLost>', 'connector=', connector, 'reason=', reason
+
+    def clientConnectionFailed(self, connector, reason):
+        print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason
+
+
 class XendMigrateInfo:
 
     # states: begin, active, failed, succeeded?
 
-    def __init__(self, id, dom, dst):
+    def __init__(self, id, dom, host, port):
         self.id = id
         self.state = 'begin'
         self.src_host = socket.gethostname()
         self.src_dom = dom
         self.dst_host = dst
+        self.dst_port = port
         self.dst_dom = None
+        self.start = 0
+        self.deferred = defer.Deferred()
         
     def set_state(self, state):
         self.state = state
@@ -29,12 +131,33 @@ class XendMigrateInfo:
         sxpr = ['migrate', ['id', self.id], ['state', self.state] ]
         sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
         sxpr.append(sxpr_src)
-        sxpr_dst = ['dst', ['host', self.dst] ]
+        sxpr_dst = ['dst', ['host', self.dst_host] ]
         if self.dst_dom:
             sxpr_dst.append(['domain', self.dst_dom])
         sxpr.append(sxpr_dst)
         return sxpr
-    
+
+    def vmconfig(self):
+        dominfo = xd.domain_get(self.dom)
+        if dominfo:
+            val = return sxp.to_string(dominfo)
+        else:
+            val = None
+        return None
+
+    def error(self, err):
+        self.state = 'error'
+
+    def ok(self, dom):
+        self.state = 'ok'
+        self.dst_dom = dom
+
+    def close(self):
+        if self.state =='ok':
+            eserver.inject('xend.migrate.ok', self.sxpr())
+        else:
+            self.state = 'error'
+            eserver.inject('xend.migrate.error', self.sxpr())
 
 class XendMigrate:
     # Represents migration in progress.
@@ -84,15 +207,15 @@ class XendMigrate:
     def migrate_get(self, id):
         return self.migrate.get(id)
     
-    def migrate_begin(self, dom, dst):
+    def migrate_begin(self, dom, host):
         # Check dom for existence, not migrating already.
-        # Create migrate info, tell xend to migrate it?
-        # - or fork migrate command ourselves?
         # Subscribe to migrate notifications (for updating).
         id = self.nextid()
-        info = XenMigrateInfo(id, dom, dst)
+        info = XenMigrateInfo(id, dom, host, XFRD_PORT)
         self._add_migrate(id, info)
-        return id
+        mcf = MigrateClientFactory(info)
+        reactor.connectTCP('localhost', XFRD_PORT, mcf)
+        return info.deferred
 
 def instance():
     global inst
diff --git a/tools/python/xen/xend/packing.py b/tools/python/xen/xend/packing.py
new file mode 100644 (file)
index 0000000..760af7f
--- /dev/null
@@ -0,0 +1,329 @@
+
+# XDR-style packer/unpacker for sxpr.
+#
+# string -> [STRING] [len:u16] <len bytes>
+# atom   -> [ATOM]   [len:u16] <len bytes>
+# int    -> [UINT]   [value]
+# list   -> [LIST]   {1 elt}* 0
+# null   -> [NULL]
+# none   -> [NONE]
+# bool   -> [BOOL]   { 0:u8 | 1:u8 }
+#
+# types packed as u16.
+#
+# So (a b c) -> [LIST] 1 a 1 b 1 c 0
+#    ()      -> [LIST] 0
+
+import struct
+
+try:
+    from cStringIO import StringIO as _StringIO
+except ImportError:
+    from StringIO import StringIO as _StringIO
+
+import types
+
+class Error(Exception):
+    
+    def __init__(self, msg):
+        self.msg = msg
+        
+    def __repr__(self):
+        return repr(self.msg)
+    
+    def __str__(self):
+        return str(self.msg)
+
+
+class ConversionError(Error):
+    pass
+
+BOOL_SIZE   = 1
+BOOL_FMT    = '>B'
+
+BYTE_SIZE   = 1
+BYTE_FMT    = '>b'
+UBYTE_FMT   = '>B'
+
+SHORT_SIZE  = 2
+SHORT_FMT   = '>h'
+USHORT_FMT  = '>H'
+
+INT_SIZE   =  4
+INT_FMT    = '>l'
+UINT_FMT   = '>L'
+
+NONE_CODE   = 0
+NULL_CODE   = 1
+INT_CODE    = 2
+STRING_CODE = 3
+ATOM_CODE   = 4
+BOOL_CODE   = 5
+LIST_CODE   = 10
+
+class Packer:
+    
+    def __init__(self, io=None):
+        self.reset(io=io)
+
+    def reset(self, io=None):
+        if io is None:
+            io = _StringIO()
+        self.io = io
+
+    def get_buffer(self):
+        return self.io.getvalue()
+
+    def get_io(self):
+        return self.io
+
+    def struct_pack(self, fmt, x):
+        try:
+            self.io.write(struct.pack(fmt, x))
+        except struct.error, msg:
+            raise ConversionError, msg
+
+    def pack_none(self):
+        pass
+    
+    def pack_bool(self, x):
+        # { '1' | '0' }
+        print 'bool>', x
+        if x:
+            self.io.write('\1')
+        else:
+            self.io.write('\0')
+
+    def pack_byte(self, x):
+        self.struct_pack(BYTE_FMT, x & 0xff)
+
+    def pack_char(self, x):
+        print 'char>', x
+        self.io.write(x)
+        
+    def pack_ubyte(self, x):
+        print 'ubyte>', x
+        self.struct_pack(UBYTE_FMT, x & 0xff)
+
+    def pack_ushort(self, x):
+        print 'ushort>', x
+        self.struct_pack(USHORT_FMT, x & 0xffff)
+        
+    def pack_short(self, x):
+        print 'short>', x
+        self.struct_pack(SHORT_FMT, x & 0xffff)
+
+    def pack_uint(self, x):
+        print 'uint>', x
+        self.struct_pack(UINT_FMT, x)
+        
+    def pack_int(self, x):
+        print 'int>', x
+        self.struct_pack(INT_FMT, x)
+
+    def pack_uhyper(self, x):
+        print 'uhyper>', x
+        self.pack_uint(x>>32 & 0xffffffffL)
+        self.pack_uint(x & 0xffffffffL)
+
+    pack_hyper = pack_uhyper
+
+    def pack_fstring(self, n, x):
+        print 'fstring>', x
+        self.io.write(x)
+
+    pack_fopaque = pack_fstring
+
+    def pack_string(self, x):
+        print 'string>', x
+        n = len(x)
+        self.pack_ushort(n)
+        self.pack_fstring(n, x)
+
+    pack_opaque = pack_string
+    pack_bytes = pack_string
+
+    def pack_list(self, x, pack_item):
+        print 'list>', x
+        # { '1' <item> }* '0'
+        for item in x:
+            self.pack_bool(1)
+            pack_item(item)
+        self.pack_bool(0)
+
+    def pack_farray(self, x, pack_item):
+        # <item>*
+        # Can pass n and check length - but is it worth it?
+        print 'farray>', list
+        for item in x:
+            pack_item(item)
+
+    def pack_array(self, x, pack_item):
+        # n <item>*n
+        print 'array>', x
+        self.pack_uint(len(x))
+        self.pack_farray(x, pack_item)
+
+class Unpacker:
+
+    def __init__(self, data):
+        self.reset(data)
+
+    def reset(self, data):
+        if isinstance(data, types.StringType):
+            data = _StringIO(data)
+        self.io = data
+
+    def get_bytes(self, n):
+        if n < 0:
+            raise ConversionError('negative byte count')
+        data = self.io.read(n)
+        return data
+
+    def struct_unpack(self, fmt, n):
+        data = self.get_bytes(n)
+        try:
+            return struct.unpack(fmt, data)[0]
+        except struct.error, msg:
+            raise ConversionError, msg
+       
+    def unpack_none(self):
+        return None
+
+    def unpack_bool(self):
+        return self.struct_unpack(BOOL_FMT, BOOL_SIZE)
+
+    def unpack_char(self):
+        return self.get_bytes(1)[0]
+
+    def unpack_byte(self):
+        return self.struct_unpack(BYTE_FMT, BYTE_SIZE)
+    
+    def unpack_ubyte(self):
+        return self.struct_unpack(UBYTE_FMT, BYTE_SIZE)
+    
+    def unpack_ushort(self):
+        return self.struct_unpack(USHORT_FMT, SHORT_SIZE)
+
+    def unpack_short(self):
+        return self.struct_unpack(SHORT_FMT, SHORT_SIZE)
+        
+    def unpack_uint(self):
+        x = self.struct_unpack(UINT_FMT, UINT_SIZE)
+        try:
+            return int(x)
+        except OverflowError:
+            return x
+
+    def unpack_int(self):
+        return self.struct_unpack(INT_FMT, INT_SIZE)
+
+    def unpack_uhyper(self):
+        hi = self.unpack_uint()
+        lo = self.unpack_uint()
+        return long(hi)<<32 | lo
+
+    def unpack_hyper(self):
+        x = self.unpack_uhyper()
+        if x >= 0x8000000000000000L:
+            x = x - 0x10000000000000000L
+        return x
+
+    def unpack_fstring(self, n):
+        return self.get_bytes(n)
+
+    unpack_fopaque = unpack_fstring
+
+    def unpack_string(self):
+        n = self.unpack_ushort()
+        return self.unpack_fstring(n)
+
+    unpack_opaque = unpack_string
+    unpack_bytes = unpack_string
+
+    def unpack_list(self, unpack_item):
+        list = []
+        while self.unpack_bool():
+            list.append(unpack_item())
+        return list
+
+    def unpack_farray(self, n, unpack_item):
+        list = []
+        for i in range(n):
+            list.append(unpack_item())
+        return list
+
+    def unpack_array(self, unpack_item):
+        n = self.unpack_ushort()
+        return self.unpack_farray(n, unpack_item)
+
+class SxpPacker(Packer):
+
+    pack_code = Packer.pack_ushort
+
+    def pack(self, x):
+        if isinstance(x, types.NoneType):
+            self.pack_code(NONE_CODE)
+            self.pack_none()
+        elif isinstance(x, types.IntType):
+            self.pack_code(INT_CODE)
+            self.pack_int(x)
+        elif isinstance(x, types.StringType):
+            self.pack_code(STRING_CODE)
+            self.pack_string(x)
+        elif isinstance(x, types.ListType):
+            self.pack_code(LIST_CODE)
+            self.pack_list(x, self.pack)
+        else:
+           raise Error('invalid type ' + str(type(x)))
+
+class SxpUnpacker(Unpacker):
+
+    unpack_code = Unpacker.unpack_ushort
+
+    def unpack(self):
+        code = self.unpack_code()
+        if code == NONE_CODE:
+            val = self.unpack_none()
+        elif code == INT_CODE:
+            val = self.unpack_int()
+        elif code == BOOL_CODE:
+            val = self.unpack_bool()
+        elif code == STRING_CODE:
+            val = self.unpack_string()
+        elif code == ATOM_CODE:
+            val = self.unpack_string()
+        elif code == LIST_CODE:
+            val = self.unpack_list(self.unpack)
+        else:
+            raise Error('invalid code ' + str(code))
+        return val
+
+def main():
+    d = "['vfarm', ['@', ['name', 'vfarm1']], ['memory', 1024], ['image', 'splinux'], ['args', 'root=/dev/nfs ip=dhcp'], [ 1, -1, 1000000]]"
+    print"> len=", len(d), "d=", d
+    obj = ['vfarm', ['@', ['name', 'vfarm1']],
+           ['memory', 1024],
+           ['image', 'splinux'],
+           ['args', 'root=/dev/nfs ip=dhcp'],
+           [ 1, -1, 1000000] ]
+    print "> obj=", obj
+    pack = SxpPacker()
+    pack.pack(obj)
+    data = pack.get_buffer()
+    print "> len=", len(data), "data=", data
+    unpack = SxpUnpacker(data)
+    obj_unpack = unpack.unpack()
+    print "> obj=", obj_unpack
+    #obj = [100,101,102, 999.00234, { 'a': 1, 'b': 2 } ]
+    #pack.reset()
+    #pack.pack_item(obj)
+    #data = pack.get_buffer()
+    #print "> obj=", obj
+    #print "> len=", len(data), "data=", data
+    #unpack.reset(data)
+    #obj_unpack = unpack.unpack_item()
+    #print "> obj=", obj_unpack
+    
+if __name__ == "__main__":
+    main()
diff --git a/tools/xfrd/Make.xfrd b/tools/xfrd/Make.xfrd
new file mode 100644 (file)
index 0000000..39f3afd
--- /dev/null
@@ -0,0 +1,34 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+
+UTIL_LIB = libutil.a
+
+UTIL_LIB_SRC =
+UTIL_LIB_SRC += allocate.c
+UTIL_LIB_SRC += enum.c
+UTIL_LIB_SRC += file_stream.c
+UTIL_LIB_SRC += gzip_stream.c
+UTIL_LIB_SRC += hash_table.c
+UTIL_LIB_SRC += iostream.c
+UTIL_LIB_SRC += lexis.c
+UTIL_LIB_SRC += lzi_stream.c
+UTIL_LIB_SRC += marshal.c
+UTIL_LIB_SRC += string_stream.c
+UTIL_LIB_SRC += sxpr.c
+#UTIL_LIB_SRC += sxpr_parser.c
+UTIL_LIB_SRC += sys_net.c
+UTIL_LIB_SRC += sys_string.c
+#UTIL_LIB_SRC += util.c
+UTIL_LIB_SRC += xdr.c
+
+#----------------------------------------------------------------------------
+# Xfrd.
+
+XFRD_PROG_SRC =
+XFRD_PROG_SRC += xfrd.c
+#XFRD_PROG_SRC += xfr_msg.c
+XFRD_PROG_SRC += xen_domain.c
+XFRD_PROG_SRC += select.c
+XFRD_PROG_SRC += connection.c
+
+#============================================================================
diff --git a/tools/xfrd/Makefile b/tools/xfrd/Makefile
new file mode 100644 (file)
index 0000000..fe84120
--- /dev/null
@@ -0,0 +1,68 @@
+# -*- mode: Makefile; -*-
+#============================================================================
+#
+# Mike Wray <mike.wray@hp.com>
+#============================================================================
+
+XEN_ROOT  = ../..
+include $(XEN_ROOT)/tools/Make.defs
+
+XFRD_INSTALL_DIR = /usr/sbin
+
+vpath %.h      $(XEN_HYPERVISOR_IFS)
+INCLUDES += -I $(XEN_HYPERVISOR_IFS)
+
+vpath %h       $(XEN_LINUX_INCLUDE)
+INCLUDES += -I $(XEN_LINUX_INCLUDE)
+
+vpath %.h      $(XEN_XU)
+INCLUDES += -I $(XEN_XU)
+
+vpath %c       $(XEN_LIBXUTIL)
+INCLUDES += -I $(XEN_LIBXUTIL)
+
+include Make.xfrd
+
+UTIL_LIB_OBJ = $(UTIL_LIB_SRC:.c=.o)
+
+XFRD_PROG_OBJ = $(XFRD_PROG_SRC:.c=.o)
+XFRD_PROG_OBJ += $(UTIL_LIB)
+
+CPPFLAGS += -D _XEN_XFR_STUB_
+
+CFLAGS += -g
+CFLAGS += -Wall
+CFALGS += -Werror
+CFLAGS += $(INCLUDES)
+# Make gcc generate dependencies.
+CFLAGS += -Wp,-MD,.$(@F).d
+PROG_DEP = .*.d
+
+#LDFLAGS += -L $(COMPRESS_DIR) -lz
+
+$(warning XFRD_PROG_OBJ= $(XFRD_PROG_OBJ))
+$(warning UTIL_LIB= $(UTIL_LIB))
+$(warning UTIL_LIB_OBJ= $(UTIL_LIB_OBJ))
+
+all: xfrd
+
+xfrd: $(XFRD_PROG_OBJ) -lz
+
+.PHONY: install
+install: xfrd
+       mkdir -p $(prefix)/$(XFRD_INSTALL_DIR)
+       install -m 0755 xfrd $(prefix)/$(XFRD_INSTALL_DIR)
+
+.PHONY: libutil
+libutil: $(UTIL_LIB)
+
+$(UTIL_LIB): $(UTIL_LIB_OBJ)
+       $(AR) rc $@ $^
+
+.PHONY: clean
+clean:
+       $(RM) *.o *.a *.so *~ xfrd
+       $(RM) $(PROG_DEP)
+
+-include $(PROG_DEP)
+
diff --git a/tools/xfrd/connection.c b/tools/xfrd/connection.c
new file mode 100644 (file)
index 0000000..26dca99
--- /dev/null
@@ -0,0 +1,163 @@
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "connection.h"
+#include "file_stream.h"
+#include "lzi_stream.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN]  %s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO]  %s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] %s" fmt, __FUNCTION__, ##args)
+
+/** Compress magic header. */
+char compress_magic[2] = { 0x1f, 0x8b };
+
+/** Plain magic header. */
+char plain_magic[2] = { 0x0, 0x0 };
+
+int Conn_read_header(int sock, int *flags){
+    int err = 0;
+    char magic[2] = {};
+    int k, n = sizeof(magic);
+    k = read(sock, magic, n);
+    if(k != n){
+        err = -EINVAL;
+        goto exit;
+    }
+    dprintf("> magic={ 0x%x, 0x%x }\n", magic[0], magic[1]);
+    if(magic[0] == compress_magic[0] && magic[1] == compress_magic[1]){
+        *flags |= CONN_READ_COMPRESS;
+        dprintf("> Using compress read.\n");
+    } else {
+        dprintf("> Using plain read.\n");
+    }
+  exit:
+    return err;
+}
+
+int Conn_write_header(int sock, int flags){
+    int err = 0;
+    if(flags & CONN_WRITE_COMPRESS){
+        dprintf("> Using compress write.\n");
+        err = write(sock, compress_magic, 2);
+    } else { 
+        dprintf("> Using plain write.\n");
+       err = write(sock, plain_magic, 2);
+    }
+    if(err == 2) err = 0;
+    return err;
+}
+
+/** Initialize a file stream from a file desciptor.
+ *
+ * @param fd file descriptor
+ * @param mode file mode
+ * @param flags control compression and buffering
+ * @param io return parameter for the stream
+ * @return 0 on success, error code otherwise
+ */
+int stream_init(int fd, const char *mode, int flags, int compress, IOStream **io){
+    int err = 0;
+    dprintf(">mode=%s flags=%x compress=%d\n", mode, flags, compress);
+    if(compress){
+        *io = lzi_stream_fdopen(fd, mode);
+    } else {
+        *io = file_stream_fdopen(fd, mode);
+    }
+    if(!*io){
+        err = -errno;
+        perror("fdopen");
+        goto exit;
+    }
+    if(1 && (flags & CONN_NOBUFFER)){
+        // Make unbuffered.
+        dprintf("> unbuffer...\n");
+        err = file_stream_setvbuf((compress ? lzi_stream_io(*io) : *io), NULL, _IONBF, 0);
+        if(err){
+            err = -errno;
+            perror("setvbuf");
+            goto exit;
+        }
+    }
+  exit:
+    if(err && *io){
+        dprintf("> close err=%d\n", err);
+        IOStream_close(*io);
+        *io = NULL;
+    }
+    dprintf("< err=%d\n", err);
+    return err;
+}
+    
+/** Initialize a connection.
+ *
+ * @param conn connection
+ * @param flags
+ * @param sock socket
+ * @param ipaddr ip address
+ * @return 0 on success, error code otherwise
+ */
+int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr){
+    int err = 0;
+    dprintf("> flags=%x\n", flags);
+    conn->addr = addr;
+    conn->sock = sock;
+    dprintf("> write stream...\n");
+    err = stream_init(sock, "w", flags, (flags & CONN_WRITE_COMPRESS), &conn->out);
+    if(err) goto exit;
+    IOStream_flush(conn->out);
+    dprintf("> read stream...\n");
+    err = stream_init(sock, "r", flags, (flags & CONN_READ_COMPRESS) , &conn->in);
+    if(err) goto exit;
+  exit:
+    if(err) eprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Open a connection.
+ *
+ * @param conn connection
+ * @param flags
+ * @param ipaddr ip address to connect to
+ * @param port port
+ * @return 0 on success, error code otherwise
+ */
+int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port){
+    int err = 0;
+    int sock;
+    struct sockaddr_in addr_in;
+    struct sockaddr *addr = (struct sockaddr *)&addr_in;
+    socklen_t addr_n = sizeof(addr_in);
+    dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port));
+    sock = socket(AF_INET, SOCK_STREAM, 0);
+    if(sock < 0){
+        err = -errno;
+        goto exit;
+    }
+    addr_in.sin_family = AF_INET;
+    addr_in.sin_addr = ipaddr;
+    addr_in.sin_port = port;
+    err = connect(sock, addr, addr_n);
+    if(err) goto exit;
+    //err = Conn_write_header(sock, flags);
+    //if(err < 0) goto exit;
+    err = Conn_init(conn, flags, sock, addr_in);
+  exit:
+    if(err) eprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Close a connection.
+ *
+ * @param conn connection
+ */
+void Conn_close(Conn *conn){
+    if(conn->in) IOStream_close(conn->in);
+    if(conn->out) IOStream_close(conn->out);
+    shutdown(conn->sock, 2);
+}
diff --git a/tools/xfrd/connection.h b/tools/xfrd/connection.h
new file mode 100644 (file)
index 0000000..790c01f
--- /dev/null
@@ -0,0 +1,32 @@
+/* $Id: connection.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */
+#ifndef _VFC_CONNECTION_H_
+#define _VFC_CONNECTION_H_
+
+#include <netinet/in.h>
+
+#include "iostream.h"
+
+/** A connection.
+ * The underlying transport is a socket. 
+ * Contains in and out streams using the socket.
+ */
+typedef struct Conn {
+    struct sockaddr_in addr;
+    int sock;
+    IOStream *in;
+    IOStream *out;
+} Conn;
+
+enum {
+    CONN_NOBUFFER=1,
+    CONN_READ_COMPRESS=2,
+    CONN_WRITE_COMPRESS=4,
+};
+    
+extern int Conn_read_header(int sock, int *flags);
+extern int Conn_write_header(int sock, int flags);
+extern int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr);
+extern int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port);
+extern void Conn_close(Conn *conn);
+
+#endif /* ! _VFC_CONNECTION_H_ */
diff --git a/tools/xfrd/debug.h b/tools/xfrd/debug.h
new file mode 100644 (file)
index 0000000..69a6b49
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef _XUTIL_DEBUG_H_
+#define _XUTIL_DEBUG_H_
+
+#ifndef MODULE_NAME
+#define MODULE_NAME ""
+#endif
+
+#ifdef __KERNEL__
+#include <linux/config.h>
+#include <linux/kernel.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) printk(KERN_DEBUG   "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO    "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR     "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) printk(KERN_INFO    "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) printk(KERN_ERR     "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#else
+
+#include <stdio.h>
+
+#ifdef DEBUG
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args)
+
+#else
+
+#define dprintf(fmt, args...) do {} while(0)
+#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args)
+#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args)
+
+#endif
+
+#endif
+
+/** Print format for an IP address.
+ * See NIPQUAD(), HIPQUAD()
+ */
+#define IPFMT "%u.%u.%u.%u"
+
+#endif /* ! _XUTIL_DEBUG_H_ */
diff --git a/tools/xfrd/enum.c b/tools/xfrd/enum.c
new file mode 100644 (file)
index 0000000..95f6e31
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#else
+#include <errno.h>
+#endif
+
+#include "sys_string.h"
+#include "enum.h"
+
+/** Map an enum name to its value using a table.
+ *
+ * @param name enum name
+ * @param defs enum definitions
+ * @return enum value or -1 if not known
+ */
+int enum_name_to_val(char *name, EnumDef *defs){
+    int val = -1;
+    for(; defs->name; defs++){
+       if(!strcmp(defs->name, name)){
+           val = defs->val;
+           break;
+       }
+    }
+    return val;
+}
+
+/** Map an enum value to its name using a table.
+ *
+ * @param val enum value
+ * @param defs enum definitions
+ * @param defs_n number of definitions
+ * @return enum name or NULL if not known
+ */
+char *enum_val_to_name(int val, EnumDef *defs){
+    char *name = NULL;
+    for(; defs->name; defs++){
+       if(val == defs->val){
+           name = defs->name;
+           break;
+       }
+    }
+    return name;
+}
+
diff --git a/tools/xfrd/enum.h b/tools/xfrd/enum.h
new file mode 100644 (file)
index 0000000..cdc0f6f
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2002, 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef _XUTIL_ENUM_H_
+#define _XUTIL_ENUM_H_
+
+/** Mapping of an enum value to a name. */
+typedef struct EnumDef {
+    int val;
+    char *name;
+} EnumDef;
+
+extern int enum_name_to_val(char *name, EnumDef *defs);
+extern char *enum_val_to_name(int val, EnumDef *defs);
+
+#endif /* _XUTIL_ENUM_H_ */
diff --git a/tools/xfrd/hash_table.c b/tools/xfrd/hash_table.c
new file mode 100644 (file)
index 0000000..13da946
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifdef __KERNEL__
+#  include <linux/config.h>
+#  include <linux/module.h>
+#  include <linux/kernel.h>
+#  include <linux/errno.h>
+#else
+#  include <errno.h>
+#  include <stddef.h>
+#endif
+
+//#include <limits.h>
+
+#include "allocate.h"
+#include "hash_table.h"
+
+/** @file
+ * Base support for hashtables.
+ *
+ * Hash codes are reduced modulo the number of buckets to index tables,
+ * so there is no need for hash functions to limit the range of hashcodes.
+ * In fact it is assumed that hashcodes do not change when the number of
+ * buckets in the table changes.
+ */
+
+/*==========================================================================*/
+/** Number of bits in half a word. */
+//#if __WORDSIZE == 64
+//#define HALF_WORD_BITS 32
+//#else
+#define HALF_WORD_BITS 16
+//#endif
+
+/** Mask for lo half of a word. On 32-bit this is 
+ * (1<<16) - 1 = 65535 = 0xffff
+ * It's 4294967295 = 0xffffffff on 64-bit.
+ */
+#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1)
+
+/** Get the lo half of a word. */
+#define LO_HALF(x) ((x) & LO_HALF_MASK)
+
+/** Get the hi half of a word. */
+#define HI_HALF(x) ((x) >> HALF_WORD_BITS)
+
+/** Do a full hash on both inputs, using DES-style non-linear scrambling.
+ * Both inputs are replaced with the results of the hash.
+ *
+ * @param pleft input/output word
+ * @param pright input/output word
+ */
+void pseudo_des(unsigned long *pleft, unsigned long *pright){
+    // Bit-rich mixing constant.
+    static const unsigned long a_mixer[] = {
+        0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, };
+
+    // Bit-rich mixing constant.
+    static const unsigned long b_mixer[] = {
+        0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, };
+
+    // Number of iterations - must be 2 or 4.
+    static const int ncycle = 4;
+    //static const int ncycle = 2;
+
+    unsigned long left = *pleft, right = *pright;
+    unsigned long v, v_hi, v_lo;
+    int i;
+
+    for(i=0; i<ncycle; i++){
+        // Flip some bits in right to get v.
+        v = right;
+        v ^= a_mixer[i];
+        // Get lo and hi halves of v.
+        v_lo = LO_HALF(v);
+        v_hi = HI_HALF(v);
+        // Non-linear mix of the halves of v.
+        v = ((v_lo * v_lo) + ~(v_hi * v_hi));
+        // Swap the halves of v.
+        v = (HI_HALF(v) | (LO_HALF(v) << HALF_WORD_BITS));
+        // Flip some bits.
+        v ^= b_mixer[i];
+        // More non-linear mixing.
+        v += (v_lo * v_hi);
+        v ^= left;
+        left = right;
+        right = v;
+    }
+    *pleft = left;
+    *pright = right;
+}
+
+/** Hash a string.
+ *
+ * @param s input to hash
+ * @return hashcode
+ */
+Hashcode hash_string(char *s){
+    Hashcode h = 0;
+    if(s){
+        for( ; *s; s++){
+            h = hash_2ul(h, *s);
+        }
+    }
+    return h;
+}
+
+/** Get the bucket for a hashcode in a hash table.
+ *
+ * @param table to get bucket from
+ * @param hashcode to get bucket for
+ * @return bucket
+ */
+inline HTBucket * get_bucket(HashTable *table, Hashcode hashcode){
+    return table->buckets + (hashcode % table->buckets_n);
+}
+
+/** Initialize a hash table.
+ * Can be safely called more than once.
+ *
+ * @param table to initialize
+ */
+void HashTable_init(HashTable *table){
+    int i;
+
+    if(!table->init_done){
+        table->init_done = 1;
+        table->next_id = 0;
+        for(i=0; i<table->buckets_n; i++){
+            HTBucket *bucket = get_bucket(table, i);
+            bucket->head = 0;
+            bucket->count = 0;
+        }
+        table->entry_count = 0;
+    }
+}
+
+/** Allocate a new hashtable.
+ * If the number of buckets is not positive the default is used.
+ * The number of buckets should usually be prime.
+ *
+ * @param buckets_n number of buckets
+ * @return new hashtable or null
+ */
+HashTable *HashTable_new(int buckets_n){
+    HashTable *z = ALLOCATE(HashTable);
+    if(!z) goto exit;
+    if(buckets_n <= 0){
+        buckets_n = HT_BUCKETS_N;
+    }
+    z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
+    if(!z->buckets){
+        deallocate(z);
+        z = 0;
+        goto exit;
+    }
+    z->buckets_n = buckets_n;
+    HashTable_init(z);
+  exit:
+    return z;
+}
+
+/** Free a hashtable.
+ * Any entries are removed and freed.
+ *
+ * @param h hashtable (ignored if null)
+ */
+void HashTable_free(HashTable *h){
+    if(h){
+        HashTable_clear(h);
+        deallocate(h->buckets);
+        deallocate(h);
+    }
+}
+
+/** Push an entry on the list in the bucket for a given hashcode.
+ *
+ * @param table to add entry to
+ * @param hashcode for the entry
+ * @param entry to add
+ */
+static inline void push_on_bucket(HashTable *table, Hashcode hashcode,
+                                 HTEntry *entry){
+    HTBucket *bucket;
+    HTEntry *old_head;
+
+    bucket = get_bucket(table, hashcode);
+    old_head = bucket->head;
+    bucket->count++;
+    bucket->head = entry;
+    entry->next = old_head;
+}
+
+/** Change the number of buckets in a hashtable.
+ * No-op if the number of buckets is not positive.
+ * Existing entries are reallocated to buckets based on their hashcodes.
+ * The table is unmodified if the number of buckets cannot be changed.
+ *
+ * @param table hashtable
+ * @param buckets_n new number of buckets
+ * @return 0 on success, error code otherwise
+ */
+int HashTable_set_buckets_n(HashTable *table, int buckets_n){
+    int err = 0;
+    HTBucket *old_buckets = table->buckets;
+    int old_buckets_n = table->buckets_n;
+    int i;
+
+    if(buckets_n <= 0){
+        err = -EINVAL;
+        goto exit;
+    }
+    table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket));
+    if(!table->buckets){
+        err = -ENOMEM;
+        table->buckets = old_buckets;
+        goto exit;
+    }
+    table->buckets_n = buckets_n;
+    for(i=0; i<old_buckets_n; i++){
+        HTBucket *bucket = old_buckets + i;
+        HTEntry *entry, *next;
+        for(entry = bucket->head; entry; entry = next){
+            next = entry->next;
+            push_on_bucket(table, entry->hashcode, entry);
+        }
+    }
+    deallocate(old_buckets);
+  exit:
+    return err;
+}
+
+/** Adjust the number of buckets so the table is neither too full nor too empty.
+ * The table is unmodified if adjusting fails.
+ *
+ * @param table hash table
+ * @param buckets_min minimum number of buckets (use default if 0 or negative)
+ * @return 0 on success, error code otherwise
+ */
+int HashTable_adjust(HashTable *table, int buckets_min){
+    int buckets_n = 0;
+    int err = 0;
+    if(buckets_min <= 0) buckets_min = HT_BUCKETS_N;
+    if(table->entry_count >= table->buckets_n){
+        // The table is dense - expand it.
+        buckets_n = 2 * table->buckets_n;
+    } else if((table->buckets_n > buckets_min) &&
+              (4 * table->entry_count < table->buckets_n)){
+        // The table is more than minimum size and sparse - shrink it.
+        buckets_n = 2 * table->entry_count;
+        if(buckets_n < buckets_min) buckets_n = buckets_min;
+    }
+    if(buckets_n){
+        err = HashTable_set_buckets_n(table, buckets_n);
+    }
+    return err;
+}
+
+/** Allocate a new entry for a given value.
+ *
+ * @param value to put in the entry
+ * @return entry, or 0 on failure
+ */
+HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){
+    HTEntry *z = ALLOCATE(HTEntry);
+    if(z){
+        z->hashcode = hashcode;
+        z->key = key;
+        z->value = value;
+    }
+    return z;
+}
+
+/** Free an entry.
+ *
+ * @param z entry to free
+ */
+inline void HTEntry_free(HTEntry *z){
+    if(z){
+        deallocate(z);
+    }
+}
+
+/** Free an entry in a hashtable.
+ * The table's entry_free_fn is used is defined, otherwise 
+ * the HTEntry itself is freed.
+ *
+ * @param table hashtable
+ * @param entry to free
+ */
+inline void HashTable_free_entry(HashTable *table, HTEntry *entry){
+    if(!entry)return;
+    if(table && table->entry_free_fn){
+        table->entry_free_fn(table, entry);
+    } else {
+        HTEntry_free(entry);
+    }
+}
+
+/** Get the first entry satisfying a test from the bucket for the
+ * given hashcode.
+ *
+ * @param table to look in
+ * @param hashcode indicates the bucket
+ * @param test_fn test to apply to elements
+ * @param arg first argument to calls to test_fn
+ * @return entry found, or 0
+ */
+inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
+                                     TableTestFn *test_fn, TableArg arg){
+    HTBucket *bucket;
+    HTEntry *entry = 0;
+    HTEntry *next;
+
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(test_fn(arg, table, entry)){
+            break;
+        }
+    }
+    return entry;
+}
+
+/** Test hashtable keys for equality.
+ * Uses the table's key_equal_fn if defined, otherwise pointer equality.
+ *
+ * @param key1 key to compare
+ * @param key2 key to compare
+ * @return 1 if equal, 0 otherwise
+ */
+inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){
+    return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2);
+}
+
+/** Compute the hashcode of a hashtable key.
+ * The table's key_hash_fn is used if defined, otherwise the address of
+ * the key is hashed.
+ *
+ * @param table hashtable
+ * @param key to hash
+ * @return hashcode
+ */
+inline Hashcode HashTable_key_hash(HashTable *table, void *key){
+    return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key));
+}
+
+/** Test if an entry has a given key.
+ *
+ * @param arg containing key to test for
+ * @param table the entry is in
+ * @param entry to test
+ * @return 1 if the entry has the key, 0 otherwise
+ */
+static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){
+    return HashTable_key_equal(table, arg.ptr, entry->key);
+}
+
+/** Get an entry with a given key.
+ *
+ * @param table to search
+ * @param key to look for
+ * @return entry if found, null otherwise
+ */
+#if 0
+inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
+    TableArg arg = { ptr: key };
+    return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg);
+}
+#else
+inline HTEntry * HashTable_get_entry(HashTable *table, void *key){
+    Hashcode hashcode;
+    HTBucket *bucket;
+    HTEntry *entry = 0;
+    HTEntry *next;
+
+    hashcode = HashTable_key_hash(table, key);
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(HashTable_key_equal(table, key, entry->key)){
+            break;
+        }
+    }
+    return entry;
+}
+#endif
+
+/** Get the value of an entry with a given key.
+ *
+ * @param table to search
+ * @param key to look for
+ * @return value if an entry was found, null otherwise
+ */
+inline void * HashTable_get(HashTable *table, void *key){
+    HTEntry *entry = HashTable_get_entry(table, key);
+    return (entry ? entry->value : 0);
+}
+
+/** Print the buckets in a table.
+ *
+ * @param table to print
+ */
+void show_buckets(HashTable *table, IOStream *io){
+    int i,j ;
+    IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n);
+    for(i=0; i<table->buckets_n; i++){
+        if(0 || table->buckets[i].count>0){
+            IOStream_print(io, "bucket %3d %3d %10p ", i,
+                        table->buckets[i].count,
+                        table->buckets[i].head);
+            for(j = table->buckets[i].count; j>0; j--){
+                IOStream_print(io, "+");
+            }
+            IOStream_print(io, "\n");
+        }
+    }
+    HashTable_print(table, io); 
+}
+    
+/** Print an entry in a table.
+ *
+ * @param entry to print
+ * @param arg a pointer to an IOStream to print to
+ * @return 0
+ */
+static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){
+    IOStream *io = (IOStream*)arg.ptr;
+    IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n",
+                entry->hashcode % table->buckets_n,
+                entry->hashcode,
+                entry->index,
+                entry, entry->key, entry->value);
+    return 0;
+}
+
+/** Print a hash table.
+ *
+ * @param table to print
+ */
+void HashTable_print(HashTable *table, IOStream *io){
+    IOStream_print(io, "{\n");
+    HashTable_map(table, print_entry, (TableArg){ ptr: io });
+    IOStream_print(io, "}\n");
+}
+/*==========================================================================*/
+
+/** Get the next entry id to use for a table.
+ *
+ * @param table hash table
+ * @return non-zero entry id
+ */
+static inline unsigned long get_next_id(HashTable *table){
+    unsigned long id;
+
+    if(table->next_id == 0){
+        table->next_id = 1;
+    }
+    id = table->next_id++;
+    return id;
+}
+
+/** Add an entry to the bucket for the
+ * given hashcode.
+ *
+ * @param table to insert in
+ * @param hashcode indicates the bucket
+ * @param key to add an entry for
+ * @param value to add an entry for
+ * @return entry on success, 0 on failure
+ */
+inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){
+    HTEntry *entry = HTEntry_new(hashcode, key, value);
+    if(entry){
+        entry->index = get_next_id(table);
+        push_on_bucket(table, hashcode, entry);
+        table->entry_count++;
+    }
+    return entry;
+}
+
+/** Move the front entry for a bucket to the correct point in the bucket order as
+ * defined by the order function. If this is called every time a new entry is added
+ * the bucket will be maintained in sorted order.
+ *
+ * @param table to modify
+ * @param hashcode indicates the bucket
+ * @param order entry comparison function
+ * @return 0 if an entry was moved, 1 if not
+ */
+int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){
+    HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL;
+    HTBucket *bucket;
+    int err = 1;
+
+    bucket = get_bucket(table, hashcode);
+    new_entry = bucket->head;
+    if(!new_entry || !new_entry->next) goto exit;
+    for(entry = new_entry->next; entry; prev = entry, entry = entry->next){
+        if(order(new_entry, entry) <= 0) break;
+    }
+    if(prev){
+        err = 0;
+        bucket->head = new_entry->next; 
+        new_entry->next = entry;
+        prev->next = new_entry;
+    }
+  exit:
+    return err;
+}
+
+/** Add an entry to a hashtable.
+ * The entry is added to the bucket for its key's hashcode.
+ *
+ * @param table to insert in
+ * @param key to add an entry for
+ * @param value to add an entry for
+ * @return entry on success, 0 on failure
+ */
+inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){
+    return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value);
+}
+
+
+/** Remove entries satisfying a test from the bucket for the
+ * given hashcode. 
+ *
+ * @param table to remove from
+ * @param hashcode indicates the bucket
+ * @param test_fn test to apply to elements
+ * @param arg first argument to calls to test_fn
+ * @return number of entries removed
+ */
+inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
+                                 TableTestFn *test_fn, TableArg arg){
+    HTBucket *bucket;
+    HTEntry *entry, *prev = 0, *next;
+    int removed_count = 0;
+
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(test_fn(arg, table, entry)){
+            if(prev){
+                prev->next = next;
+            } else {
+                bucket->head = next;
+            }
+            bucket->count--;
+            table->entry_count--;
+            removed_count++;
+            HashTable_free_entry(table, entry);
+            entry = 0;
+        }
+        prev = entry;
+    }
+    return removed_count;
+}
+
+/** Remove entries with a given key. 
+ *
+ * @param table to remove from
+ * @param key of entries to remove
+ * @return number of entries removed
+ */
+inline int HashTable_remove(HashTable *table, void *key){
+#if 1
+    Hashcode hashcode;
+    HTBucket *bucket;
+    HTEntry *entry, *prev = 0, *next;
+    int removed_count = 0;
+
+    hashcode = HashTable_key_hash(table, key);
+    bucket = get_bucket(table, hashcode);
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        if(HashTable_key_equal(table, key, entry->key)){
+            if(prev){
+                prev->next = next;
+            } else {
+                bucket->head = next;
+            }
+            bucket->count--;
+            table->entry_count--;
+            removed_count++;
+            HashTable_free_entry(table, entry);
+            entry = 0;
+        }
+        prev = entry;
+    }
+    return removed_count;
+#else
+    return HashTable_remove_entry(table, HashTable_key_hash(table, key),
+                                 has_key, (TableArg){ ptr: key});
+#endif
+}
+
+/** Remove (and free) all the entries in a bucket.
+ *
+ * @param bucket to clear
+ */
+static inline void bucket_clear(HashTable *table, HTBucket *bucket){
+    HTEntry *entry, *next;
+
+    for(entry = bucket->head; entry; entry = next){
+        next = entry->next;
+        HashTable_free_entry(table, entry);
+    }
+    bucket->head = 0;
+    table->entry_count -= bucket->count;
+    bucket->count = 0;
+}
+
+/** Remove (and free) all the entries in a table.
+ *
+ * @param table to clear
+ */
+void HashTable_clear(HashTable *table){
+    int i, n = table->buckets_n;
+
+    for(i=0; i<n; i++){
+        bucket_clear(table, table->buckets + i);
+    }
+}
diff --git a/tools/xfrd/hash_table.h b/tools/xfrd/hash_table.h
new file mode 100644 (file)
index 0000000..6608b49
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2001 - 2004 Mike Wray <mike.wray@hp.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XUTIL_HASH_TABLE_H_
+#define _XUTIL_HASH_TABLE_H_
+
+#include "iostream.h"
+
+typedef unsigned long Hashcode;
+
+/** Type used to pass parameters to table functions. */
+typedef union TableArg {
+    unsigned long ul;
+    void *ptr;
+} TableArg;
+
+/** An entry in a bucket list. */
+typedef struct HTEntry {
+    /** Hashcode of the entry's key. */
+    Hashcode hashcode;
+    /** Identifier for this entry in the table. */
+    int index;
+    /** The key for this entry. */
+    void *key;
+    /** The value in this entry. */
+    void *value;
+    /** The next entry in the list. */
+    struct HTEntry *next;
+} HTEntry;
+
+/** A bucket in a rule table. */
+typedef struct HTBucket {
+    /** Number of entries in the bucket. */
+    int count;
+    /** First entry in the bucket (may be null). */
+    HTEntry *head;
+} HTBucket;
+
+/** Default number of buckets in a hash table.
+ * You want enough buckets so the lists in the buckets will typically be short.
+ * It's a good idea if this is prime, since that will help to spread hashcodes
+ * around the table.
+ */
+//#define HT_BUCKETS_N 1
+//#define HT_BUCKETS_N 3
+//#define HT_BUCKETS_N 7
+//#define HT_BUCKETS_N 17
+//#define HT_BUCKETS_N 97
+//#define HT_BUCKETS_N 211
+//#define HT_BUCKETS_N 401
+#define HT_BUCKETS_N 1021
+
+typedef struct HashTable HashTable;
+
+/** Type for a function used to select table entries. */
+typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry);
+
+/** Type for a function to map over table entries. */
+typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry);
+
+/** Type for a function to free table entries. */
+typedef void TableFreeFn(HashTable *table, HTEntry *entry);
+
+/** Type for a function to hash table keys. */
+typedef Hashcode TableHashFn(void *key);
+
+/** Type for a function to test table keys for equality. */
+typedef int TableEqualFn(void *key1, void *key2);
+
+/** Type for a function to order table entries. */
+typedef int TableOrderFn(HTEntry *e1, HTEntry *e2);
+
+/** General hash table.
+ * A hash table with a list in each bucket.
+ * Functions can be supplied for freeing entries, hashing keys, and comparing keys.
+ * These all default to 0, when default behaviour treating keys as integers is used.
+ */
+struct HashTable {
+    /** Flag indicating whether the table has been initialised. */
+    int init_done;
+    /** Next value for the id field in inserted rules. */
+    unsigned long next_id;
+    /** Number of buckets in the bucket array. */
+    int buckets_n;
+    /** Array of buckets, each with its own list. */
+    HTBucket *buckets;
+    /** Number of entries in the table. */
+    int entry_count;
+    /** Function to free keys and values in entries. */
+    TableFreeFn *entry_free_fn;
+    /** Function to hash keys. */
+    TableHashFn *key_hash_fn;
+    /** Function to compare keys for equality. */
+    TableEqualFn *key_equal_fn;
+    /** Place for the user of the table to hang extra data. */
+    void *user_data;
+};
+
+extern HashTable *HashTable_new(int bucket_n);
+extern void HashTable_free(HashTable *table);
+extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value);
+extern void HTEntry_free(HTEntry *entry);
+extern int HashTable_set_bucket_n(HashTable *table, int bucket_n);
+extern void HashTable_clear(HashTable *table);
+extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value);
+extern HTEntry * HashTable_get_entry(HashTable *table, void *key);
+extern HTEntry * HashTable_add(HashTable *table, void *key, void *value);
+extern void * HashTable_get(HashTable *table, void *key);
+extern int HashTable_remove(HashTable *table, void *key);
+extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode,
+                                      TableTestFn *test_fn, TableArg arg);
+extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode,
+                                   TableTestFn *test_fn, TableArg arg);
+//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg);
+extern void HashTable_print(HashTable *table, IOStream *out);
+extern int HashTable_set_buckets_n(HashTable *table, int buckets_n);
+extern int HashTable_adjust(HashTable *table, int buckets_min);
+extern void pseudo_des(unsigned long *pleft, unsigned long *pright);
+extern Hashcode hash_string(char *s);
+
+extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order);
+
+/** Control whether to use hashing based on DES or simple
+ * hashing. DES hashing is `more random' but much more expensive.
+ */
+#define HASH_PSEUDO_DES 0
+
+/** Hash a long using a quick and dirty linear congruential random number generator.
+ *  See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator".
+ *
+ * @param a value to hash
+ * @return hashed input
+ */
+static inline unsigned long lcrng_hash(unsigned long a){
+    return (1664525L * a + 1013904223L);
+}
+
+/** Hash an unsigned long.
+ *
+ * @param a input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_ul(unsigned long a){
+#if HASH_PSEUDO_DES
+    unsigned long left = a;
+    unsigned long right = 0L;
+    pseudo_des(&left, &right);
+    return right;
+#else
+    a = lcrng_hash(a);
+    a = lcrng_hash(a);
+    return a;
+#endif
+}
+
+/** Hash two unsigned longs together.
+ *
+ * @param a input to hash
+ * @param b input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_2ul(unsigned long a, unsigned long b){
+#if HASH_PSEUDO_DES
+    unsigned long left = a;
+    unsigned long right = b;
+    pseudo_des(&left, &right);
+    return right;
+#else
+    a = lcrng_hash(a);
+    a ^= b;
+    a = lcrng_hash(a);
+    return a;
+#endif
+}
+
+/** Hash a hashcode and an unsigned long together.
+ *
+ * @param a input hashcode
+ * @param b input to hash
+ * @return hashcode
+ */
+static inline Hashcode hash_hul(Hashcode a, unsigned long b){
+#if HASH_PSEUDO_DES
+    unsigned long left = a;
+    unsigned long right = b;
+    pseudo_des(&left, &right);
+    return right;
+#else
+    a ^= b;
+    a = lcrng_hash(a);
+    return a;
+#endif
+}
+
+/** Macro to declare variables for HashTable_for_each() to use.
+ *
+ * @param entry variable that is set to entries in the table
+ */
+#define HashTable_for_decl(entry) \
+  HashTable *_var_table; \
+  HTBucket *_var_bucket; \
+  HTBucket *_var_end; \
+  HTEntry *_var_next; \
+  HTEntry *entry
+
+/** Macro to iterate over the entries in a hashtable.
+ * Must be in a scope where HashTable_for_decl() has been used to declare
+ * variables for it to use.
+ * The variable 'entry' is iterated over entries in the table.
+ * The code produced is syntactically a loop, so it must be followed by
+ * a loop body, typically some statements in braces:
+ * HashTable_for_each(entry, table){ ...loop body... }
+ *
+ * HashTable_for_each() and HashTable_for_decl() cannot be used for nested
+ * loops as variables will clash.
+ *
+ * @note The simplest way to code a direct loop over the entries in a hashtable
+ * is to use a loop over the buckets, with a nested loop over the entries
+ * in a bucket. Using this approach in a macro means the macro contains
+ * an opening brace, and calls to it must be followed by 2 braces!
+ * To avoid this the code has been restructured so that it is a for loop.
+ * So that statements could be used in the test expression of the for loop,
+ * we have used the gcc statement expression extension ({ ... }).
+ *
+ * @param entry variable to iterate over the entries
+ * @param table to iterate over (non-null)
+ */
+#define HashTable_for_each(entry, table) \
+  _var_table = table; \
+  _var_bucket = _var_table->buckets; \
+  _var_end = _var_bucket + _var_table->buckets_n; \
+  for(entry=0, _var_next=0; \
+      ({ if(_var_next){ \
+             entry = _var_next; \
+             _var_next = entry->next; \
+          } else { \
+             while(_var_bucket < _var_end){ \
+                 entry = _var_bucket->head; \
+                 _var_bucket++; \
+                 if(entry){ \
+                      _var_next = entry->next; \
+                      break; \
+                 } \
+             } \
+          }; \
+         entry; }); \
+      entry = _var_next )
+
+/** Map a function over the entries in a table.
+ * Mapping stops when the function returns a non-zero value.
+ * Uses the gcc statement expression extension ({ ... }).
+ *
+ * @param table to map over
+ * @param fn function to apply to entries
+ * @param arg first argument to call the function with
+ * @return 0 if fn always returned 0, first non-zero value otherwise
+ */
+#define HashTable_map(table, fn, arg) \
+  ({ HashTable_for_decl(_var_entry); \
+    TableArg _var_arg = arg; \
+    int _var_value = 0; \
+    HashTable_for_each(_var_entry, table){ \
+        if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \
+    } \
+    _var_value; })
+
+/** Cast x to the type for a key or value in a hash table.
+ * This avoids compiler warnings when using short integers
+ * as keys or values (especially on 64-bit platforms).
+ */
+#define HKEY(x) ((void*)(unsigned long)(x))
+
+/** Cast x from the type for a key or value in a hash table.
+ * to an unsigned long. This avoids compiler warnings when using
+ * short integers as keys or values (especially on 64-bit platforms).
+ */
+#define HVAL(x) ((unsigned long)(x))
+
+#endif /* !_XUTIL_HASH_TABLE_H_ */
diff --git a/tools/xfrd/lexis.c b/tools/xfrd/lexis.c
new file mode 100644 (file)
index 0000000..26d2ec4
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/** @file
+ * Lexical analysis.
+ */
+
+#include "sys_string.h"
+#include "lexis.h"
+#include <errno.h>
+
+/** Check if a value lies in a (closed) range.
+ *
+ * @param x value to test
+ * @param lo low end of the range
+ * @param hi high end of the range
+ * @return 1 if x is in the interval [lo, hi], 0 otherwise
+ */
+inline static int in_range(int x, int lo, int hi){
+    return (lo <= x) && (x <= hi);
+}
+
+/** Determine if a string is an (unsigned) decimal number.
+ * 
+ * @param s pointer to characters to test
+ * @param n length of string
+ * @return 1 if s is a decimal number, 0 otherwise.
+ */
+int is_decimal_number(const char *s, int n){
+    int i;
+    if(n <= 0)return 0;
+    for(i = 0; i < n; i++){
+        if(!in_decimal_digit_class(s[i])) return 0;
+    }
+    return 1;
+}
+
+/** Determine if a string is a hex number.
+ * Hex numbers are 0, or start with 0x or 0X followed
+ * by a non-zero number of hex digits (0-9,a-f,A-F).
+ * 
+ * @param s pointer to characters to test
+ * @param n length of string
+ * @return 1 if s is a hex number, 0 otherwise.
+ */
+int is_hex_number(const char *s, int n){
+    int i;
+    if(n <= 0) return 0;
+    if(n == 1){
+        return s[0]=='0';
+    }
+    if(n <= 3) return 0;
+    if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0;
+    for(i = 2; i < n; i++){
+        if(!in_hex_digit_class(s[i])) return 0;
+    }
+    return 1;
+}
+
+/** Test if a string matches a keyword.
+ * The comparison is case-insensitive.
+ * The comparison fails if either argument is null.
+ *
+ * @param s string
+ * @param k keyword
+ * @return 1 if they match, 0 otherwise
+ */
+int is_keyword(const char *s, const char *k){
+  return s && k && !strcasecmp(s, k);
+}
+
+/** Test if a string matches a character.
+ *
+ * @param s string
+ * @param c character (non-null)
+ * @return 1 if s contains exactly c, 0 otherwise
+ */
+int is_keychar(const char *s, char c){
+  return c && (s[0] == c) && !s[1];
+}
diff --git a/tools/xfrd/lexis.h b/tools/xfrd/lexis.h
new file mode 100644 (file)
index 0000000..796a8a4
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef _XUTIL_LEXIS_H_
+#define _XUTIL_LEXIS_H_
+
+#include "sys_string.h"
+
+#ifdef __KERNEL__
+#  include <linux/ctype.h>
+#else
+#  include <ctype.h>
+#endif
+
+/** @file
+ * Lexical analysis.
+ */
+
+/** Class of characters treated as space. */
+#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 })
+
+/** Class of separator characters. */
+#define sep_class "{}()<>[]@!;"
+
+#define comment_class "#"
+
+/** Determine if a character is in a given class.
+ * 
+ * @param c character to test
+ * @param s null-terminated string of characters in the class
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_class(int c, const char *s){
+  return s && (strchr(s, c) != 0);
+}
+
+/** Determine if a character is in the space class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_space_class(int c){
+    return in_class(c, space_class);
+}
+
+static inline int in_comment_class(int c){
+    return in_class(c, comment_class);
+}
+
+/** Determine if a character is in the separator class.
+ * Separator characters terminate tokens, and do not need space
+ * to separate them.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_sep_class(int c){
+    return in_class(c, sep_class);
+}
+
+/** Determine if a character is in the alpha class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_alpha_class(int c){
+    return isalpha(c);
+}
+
+/** Determine if a character is in the octal digit class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_octal_digit_class(int c){
+    return '0' <= c && c <= '7';
+}
+
+/** Determine if a character is in the decimal digit class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_decimal_digit_class(int c){
+    return isdigit(c);
+}
+
+/** Determine if a character is in the hex digit class.
+ * 
+ * @param c character to test
+ * @return 1 if c is in the class, 0 otherwise.
+ */
+static inline int in_hex_digit_class(int c){
+    return isdigit(c) || in_class(c, "abcdefABCDEF");
+}
+
+
+static inline int in_string_quote_class(int c){
+    return in_class(c, "'\"");
+}
+
+static inline int in_printable_class(int c){
+    return ('A' <= c && c <= 'Z')
+        || ('a' <= c && c <= 'z')
+        || ('0' <= c && c <= '9')
+        || in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~");
+}
+
+extern int is_decimal_number(const char *s, int n);
+extern int is_hex_number(const char *s, int n);
+extern int is_keyword(const char *s, const char *k);
+extern int is_keychar(const char *s, char c);
+
+#endif /* !_XUTIL_LEXIS_H_ */
diff --git a/tools/xfrd/lzi_stream.c b/tools/xfrd/lzi_stream.c
new file mode 100644 (file)
index 0000000..5fbec77
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/** @file
+ * An IOStream implementation using LZI to provide compression and decompression.
+ * This is designed to provide compression without output latency.
+ * Flushing an LZI stream flushes all pending data to the underlying stream.
+ * This is essential for stream-based (e.g. networked) applications.
+ *
+ * A compressed data stream is a sequence of blocks.
+ * Each block is the block size followed by the compressed data.
+ * The last block has size zero.
+ * Sizes are 4-byte unsigned in network order.
+ *
+ * This format allows compressed data to be read from a stream without reading
+ * past the logical end of compressed data.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+#ifndef __KERNEL__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include "zlib.h"
+
+#include "allocate.h"
+#include "lzi_stream.h"
+#include "file_stream.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) fprintf(stderr, "[WARN]  LZI>%s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) fprintf(stdout, "[INFO]  LZI>%s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args)
+
+static int lzi_read(IOStream *s, void *buf, size_t n);
+static int lzi_write(IOStream *s, const void *buf, size_t n);
+static int lzi_error(IOStream *s);
+static int lzi_close(IOStream *s);
+static void lzi_free(IOStream *s);
+static int lzi_flush(IOStream *s);
+
+enum {
+    LZI_WRITE = 1,
+    LZI_READ = 2,
+};
+
+/** Methods used by a gzFile* IOStream. */
+static const IOMethods lzi_methods = {
+    read:  lzi_read,
+    write: lzi_write,
+    error: lzi_error,
+    close: lzi_close,
+    free:  lzi_free,
+    flush: lzi_flush,
+};
+
+#define BUFFER_SIZE (512 * 1024)
+
+typedef struct LZIState {
+    z_stream zstream;
+    void *inbuf;
+    uint32_t inbuf_size;
+    void *outbuf;
+    uint32_t outbuf_size;
+    /** Underlying stream for I/O. */
+    IOStream *io;
+    /** Flags. */
+    int flags;
+    /** Error indicator. */
+    int error;
+    int eof;
+    int plain_bytes;
+    int comp_bytes;
+    int zstream_initialized;
+    int flushed;
+} LZIState;
+
+static inline int LZIState_writeable(LZIState *s){
+    return (s->flags & LZI_WRITE) != 0;
+}
+
+static inline int LZIState_readable(LZIState *s){
+    return (s->flags & LZI_READ) != 0;
+}
+
+void LZIState_free(LZIState *z){
+    if(!z) return;
+    if(z->zstream_initialized){
+        if(LZIState_writeable(z)){
+            deflateEnd(&z->zstream);
+        } else if(LZIState_readable(z)){
+            inflateEnd(&z->zstream);
+        }
+    }
+    deallocate(z->inbuf);
+    deallocate(z->outbuf);
+    deallocate(z);
+}
+
+static int mode_flags(const char *mode, int *flags){
+    int err = 0;
+    int r=0, w=0;
+    if(!mode){
+        err = -EINVAL;
+        goto exit;
+    }
+    for(; *mode; mode++){
+        if(*mode == 'w') w = 1;
+        if(*mode == 'r') r = 1;
+    }
+    if(r + w != 1){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(r) *flags |= LZI_READ;
+    if(w) *flags |= LZI_WRITE;
+  exit:
+    return err;
+}
+
+/** Get the stream state.
+ * 
+ * @param s lzi stream
+ * @return stream state.
+ */
+static inline LZIState * lzi_state(IOStream *io){
+    return (LZIState*)io->data;
+}
+
+IOStream *lzi_stream_io(IOStream *io){
+    LZIState *s = lzi_state(io);
+    return s->io;
+}
+
+static inline void set_error(LZIState *s, int err){
+    if(err < 0 && !s->error){
+        s->error = err;
+    }
+}
+
+static int zerror(LZIState *s, int err){
+    if(err){
+        //dprintf("> err=%d\n", err);
+        if(err < 0) set_error(s, -EIO);
+    }
+    return s->error;
+}
+
+int lzi_stream_plain_bytes(IOStream *io){
+    LZIState *s = lzi_state(io);
+    return s->plain_bytes;
+}
+
+int lzi_stream_comp_bytes(IOStream *io){
+    LZIState *s = lzi_state(io);
+    return s->comp_bytes;
+}
+
+float lzi_stream_ratio(IOStream *io){
+    LZIState *s = lzi_state(io);
+    float ratio = 0.0;
+    if(s->comp_bytes){
+        ratio = ((float) s->comp_bytes)/((float) s->plain_bytes);
+    }
+    return ratio;
+}
+
+static int alloc(void **p, int n){
+    *p = allocate(n);
+    return (p ? 0 : -ENOMEM);
+}
+
+LZIState * LZIState_new(IOStream *io, int flags){
+    int err = -ENOMEM;
+    int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest.
+    int zstrategy = Z_DEFAULT_STRATEGY;
+    int zwindow = MAX_WBITS;
+    int zmemory = 8;
+    LZIState *z = ALLOCATE(LZIState);
+
+    //dprintf(">\n");
+    if(!z) goto exit;
+    z->io = io;
+    z->flags = flags;
+
+    if(LZIState_writeable(z)){
+        z->outbuf_size = BUFFER_SIZE;
+        /* windowBits is passed < 0 to suppress zlib header */
+        err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy);
+        if (err != Z_OK) goto exit;
+        z->zstream_initialized = 1;
+        err = alloc(&z->outbuf, z->outbuf_size);
+        if(err) goto exit;
+        z->zstream.next_out = z->outbuf;
+        z->zstream.avail_out = z->outbuf_size;
+    } else {
+        z->inbuf_size = BUFFER_SIZE;
+        err = alloc(&z->inbuf, z->inbuf_size);
+        if(err) goto exit;
+        ///z->zstream.next_in  = z->inbuf;
+
+        /* windowBits is passed < 0 to tell that there is no zlib header.
+         * Note that in this case inflate *requires* an extra "dummy" byte
+         * after the compressed stream in order to complete decompression and
+         * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+         * present after the compressed stream.
+         */
+        err = inflateInit2(&z->zstream, -zwindow);
+        if(err != Z_OK) goto exit;
+        z->zstream_initialized = 1;
+    }
+        
+  exit:
+    if(err){
+        LZIState_free(z);
+        z = NULL;
+    }
+    //dprintf("< z=%p\n", z);
+    return z;
+}
+
+int read_block(LZIState *s){
+    int err = 0, k = 0;
+    //dprintf(">\n");
+    if(s->eof) goto exit;
+    err = unmarshal_uint32(s->io, &k);
+    if(err) goto exit;
+    if(k > s->inbuf_size){
+        err = -EINVAL;
+        goto exit;
+    }
+    if(k){
+        err = unmarshal_bytes(s->io, s->inbuf, k);
+        if(err) goto exit;
+    } else {
+        s->eof = 1;
+    }        
+    s->zstream.avail_in = k;
+    s->zstream.next_in = s->inbuf;
+    s->comp_bytes += 4;
+    s->comp_bytes += k;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int write_block(LZIState *s){
+    int err = 0;
+    int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf);
+    //int k2 = s->outbuf_size - s->zstream.avail_out;
+    //dprintf("> k=%d k2=%d\n", k, k2);
+    if(!k) goto exit;
+    err = marshal_uint32(s->io, k);
+    if(err) goto exit;
+    err = marshal_bytes(s->io, s->outbuf, k);
+    if(err) goto exit;
+    s->zstream.next_out = s->outbuf;
+    s->zstream.avail_out = s->outbuf_size;
+    s->comp_bytes += 4;
+    s->comp_bytes += k;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int write_terminator(LZIState *s){
+    int err = 0;
+    char c = 0;
+    err = marshal_uint32(s->io, 1);
+    if(err) goto exit;
+    err = marshal_bytes(s->io, &c, 1);
+    if(err) goto exit;
+    err = marshal_uint32(s->io, 0);
+    if(err) goto exit;
+    s->comp_bytes += 9;
+  exit:
+    return err;
+}
+
+/** Write to the underlying stream using fwrite();
+ *
+ * @param io destination
+ * @param buf data
+ * @param n number of bytes to write
+ * @return number of bytes written
+ */
+static int lzi_write(IOStream *io, const void *buf, size_t n){
+    int err = 0;
+    LZIState *s = lzi_state(io);
+
+    //dprintf("> buf=%p n=%d\n", buf, n);
+    if(!LZIState_writeable(s)){
+        err = -EINVAL;
+        goto exit;
+    }
+    s->flushed = 0;
+    s->zstream.next_in = (void*)buf;
+    s->zstream.avail_in = n;
+    while(s->zstream.avail_in){
+        if(s->zstream.avail_out == 0){
+            err = write_block(s);
+            if(err) goto exit;
+        }
+        //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
+        err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH));
+        //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
+        if(err) goto exit;
+    }
+    err = n;
+    s->plain_bytes += n;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+
+/** Read from the underlying stream.
+ *
+ * @param io input
+ * @param buf where to put input
+ * @param n number of bytes to read
+ * @return number of bytes read
+ */
+static int lzi_read(IOStream *io, void *buf, size_t n){
+    int err, zerr;
+    LZIState *s = lzi_state(io);
+
+    //dprintf("> n=%d\n", n);
+    if(!LZIState_readable(s)){
+        err = -EINVAL;
+        goto exit;
+    }
+    s->zstream.next_out = buf;
+    s->zstream.avail_out = n;
+    while(s->zstream.avail_out){
+        if(s->zstream.avail_in == 0){
+            err = read_block(s);
+        }
+        //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        zerr = inflate(&s->zstream, Z_NO_FLUSH);
+        //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        if(zerr == Z_STREAM_END) break;
+        //dprintf("> zerr=%d\n", zerr);
+        err = zerror(s, zerr);
+        if(err) goto exit;
+    }
+    err = n - s->zstream.avail_out;
+    s->plain_bytes += err;
+  exit:
+    set_error(s, err);
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+static int flush_output(LZIState *s, int mode){
+    int err = 0, zerr;
+    int done = 0;
+    int avail_out_old;
+
+    //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+    if(s->flushed == 1 + mode) goto exit;
+    //s->zstream.avail_in = 0; /* should be zero already anyway */
+    for(;;){
+        // Write any available output.
+        if(done || s->zstream.avail_out == 0){
+            err = write_block(s);
+            if(err) goto exit;
+            if(done) break;
+        }
+        //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        avail_out_old = s->zstream.avail_out;
+        zerr = deflate(&s->zstream, mode);
+        err = zerror(s, zerr);
+        //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
+        //dprintf("> deflate=%d\n", err);
+        //done = (s->zstream.avail_out != 0);
+        //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old);
+        if(0 && mode == Z_FINISH){
+            done = (zerr ==  Z_STREAM_END);
+        } else {
+            done = (s->zstream.avail_in == 0)
+                //&& (s->zstream.avail_out == avail_out_old)
+                && (s->zstream.avail_out != 0);
+        }
+    }
+    s->flushed = 1 + mode;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Flush any pending input to the underlying stream.
+ *
+ * @param s lzi stream
+ * @return 0 on success, error code otherwise
+ */
+static int lzi_flush(IOStream *io){
+    int err = 0;
+    LZIState *s = lzi_state(io);
+    //dprintf(">\n");
+    if(!LZIState_writeable(s)){
+        err = -EINVAL;
+        goto exit;
+    }
+    err = flush_output(s, Z_SYNC_FLUSH);
+    if(err) goto exit;
+    err = IOStream_flush(s->io);
+  exit:
+    set_error(s, err);
+    //dprintf("< err=%d\n", err);
+    return (err < 0 ? err : 0);
+}
+
+/** Check if a stream has an error.
+ *
+ * @param s lzi stream
+ * @return code if has an error, 0 otherwise
+ */
+static int lzi_error(IOStream *s){
+    int err = 0;
+    LZIState *state = lzi_state(s);
+    err = state->error;
+    if(err) goto exit;
+    err = IOStream_error(state->io);
+  exit:
+    return err;
+}
+
+/** Close an lzi stream.
+ *
+ * @param s lzi stream to close
+ * @return result of the close
+ */
+static int lzi_close(IOStream *io){
+    int err = 0;
+    LZIState *s = lzi_state(io);
+    if(LZIState_writeable(s)){
+        err = flush_output(s, Z_FINISH);
+        if(err) goto exit;
+        err = write_terminator(s);
+        if(err) goto exit;
+        err = IOStream_flush(s->io);
+    }   
+  exit:
+    err = IOStream_close(s->io);
+    set_error(s, err);
+    return err;
+}
+
+/** Free an lzi stream.
+ *
+ * @param s lzi stream
+ */
+static void lzi_free(IOStream *s){
+    LZIState *state = lzi_state(s);
+    IOStream_free(state->io);
+    LZIState_free(state);
+    s->data = NULL;
+}
+
+/** Create an lzi stream for an IOStream.
+ *
+ * @param io stream to wrap
+ * @return new IOStream using f for i/o
+ */
+IOStream *lzi_stream_new(IOStream *io, const char *mode){
+    int err = -ENOMEM;
+    int flags = 0;
+    IOStream *zio = NULL;
+    LZIState *state = NULL;
+
+    zio = ALLOCATE(IOStream);
+    if(!zio) goto exit;
+    err = mode_flags(mode, &flags);
+    if(err) goto exit;
+    state = LZIState_new(io, flags);
+    if(!state) goto exit;
+    err = 0;
+    zio->data = state;
+    zio->methods = &lzi_methods;
+  exit:
+    if(err){
+        if(state) LZIState_free(state);
+        if(zio) deallocate(zio);
+        zio = NULL;
+    }
+    return zio;
+}
+
+/** IOStream version of fdopen().
+ *
+ * @param fd file descriptor
+ * @param flags giving the mode to open in (as for fdopen())
+ * @return new stream for the open file, or NULL if failed
+ */
+IOStream *lzi_stream_fdopen(int fd, const char *mode){
+    int err = -ENOMEM;
+    IOStream *io = NULL, *zio = NULL;
+    io = file_stream_fdopen(fd, mode);
+    if(!io) goto exit;
+    zio = lzi_stream_new(io, mode);
+    if(!io) goto exit;
+    err = 0;
+  exit:
+    if(err){
+        IOStream_free(io);
+        IOStream_free(zio);
+        zio = NULL;
+    }
+    return zio;
+}
+#endif
diff --git a/tools/xfrd/lzi_stream.h b/tools/xfrd/lzi_stream.h
new file mode 100644 (file)
index 0000000..959059e
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2003 Hewlett-Packard Company.
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _XUTIL_LZI_STREAM_H_
+#define _XUTIL_LZI_STREAM_H_
+
+#ifndef __KERNEL__
+#include "iostream.h"
+
+extern IOStream *lzi_stream_new(IOStream *io, const char *mode);
+extern IOStream *lzi_stream_fopen(const char *file, const char *mode);
+extern IOStream *lzi_stream_fdopen(int fd, const char *mode);
+extern IOStream *lzi_stream_io(IOStream *zio);
+
+extern int lzi_stream_plain_bytes(IOStream *io);
+extern int lzi_stream_comp_bytes(IOStream *io);
+extern float lzi_stream_ratio(IOStream *io);
+
+#endif
+#endif /* !_XUTIL_LZI_STREAM_H_ */
diff --git a/tools/xfrd/marshal.c b/tools/xfrd/marshal.c
new file mode 100644 (file)
index 0000000..21691d4
--- /dev/null
@@ -0,0 +1,207 @@
+#include <errno.h>
+#include "sys_net.h"
+#include "allocate.h"
+#include "marshal.h"
+
+#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args)
+#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN]  %s" fmt, __FUNCTION__, ##args)
+#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO]  %s" fmt, __FUNCTION__, ##args)
+#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args)
+
+
+#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0]))
+
+/* Messages are coded as msgid followed by message fields.
+ * Initial message on any channel is hello - so can check version
+ * compatibility.
+ *
+ * char* -> uint16_t:n <n bytes>
+ * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes).
+ * optional fields go as '1' <val> or '0' (the 0/1 is 1 byte).
+ * lists go as ('1' <elt>)* '0'
+ */
+
+int marshal_flush(IOStream *io){
+    int err  = 0;
+    err = IOStream_flush(io);
+    return err;
+}
+
+int marshal_bytes(IOStream *io, void *s, uint32_t s_n){
+    int err = 0;
+    int n;
+    n = IOStream_write(io, s, s_n);
+    if(n < 0){
+        err = n;
+    } else if (n < s_n){
+        wprintf("> Wanted %d, got %d\n", s_n, n);
+        err = -EIO;
+    }
+    return err;
+}
+
+int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){
+    int err = 0;
+    int n;
+    //dprintf("> s_n=%d\n", s_n);
+    n = IOStream_read(io, s, s_n);
+    //dprintf("> n=%d\n", n);
+    if(n < 0){
+        err = n;
+    } else if(n < s_n){
+        wprintf("> Wanted %d, got %d\n", s_n, n);
+        err = -EIO;
+    }
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int marshal_uint8(IOStream *io, uint8_t x){
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint8(IOStream *io, uint8_t *x){
+    return unmarshal_bytes(io, x, sizeof(*x));
+}
+
+int marshal_uint16(IOStream *io, uint16_t x){
+    x = htons(x);
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_uint16(IOStream *io, uint16_t *x){
+    int err = 0;
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    *x = ntohs(*x);
+    return err;
+}
+
+int marshal_int32(IOStream *io, int32_t x){
+    int err = 0;
+    //dprintf("> x=%d\n", x);
+    x = htonl(x);
+    err = marshal_bytes(io, &x, sizeof(x));
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unmarshal_int32(IOStream *io, int32_t *x){
+    int err = 0;
+    //dprintf(">\n");
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    *x = ntohl(*x);
+    //dprintf("< err=%d x=%d\n", err, *x);
+    return err;
+}
+
+int marshal_uint32(IOStream *io, uint32_t x){
+    int err = 0;
+    //dprintf("> x=%u\n", x);
+    x = htonl(x);
+    err = marshal_bytes(io, &x, sizeof(x));
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unmarshal_uint32(IOStream *io, uint32_t *x){
+    int err = 0;
+    //dprintf(">\n");
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    *x = ntohl(*x);
+    //dprintf("< err=%d x=%u\n", err, *x);
+    return err;
+}
+
+int marshal_uint64(IOStream *io, uint64_t x){
+    int err;
+    err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff));
+    if(err) goto exit;
+    err = marshal_uint32(io, (uint32_t) ( x        & 0xffffffff));
+  exit:
+    return err;
+}
+
+int unmarshal_uint64(IOStream *io, uint64_t *x){
+    int err = 0;
+    uint32_t hi, lo;
+    err = unmarshal_uint32(io, &hi);
+    if(err) goto exit;
+    err = unmarshal_uint32(io, &lo);
+    *x = (((uint64_t) hi) << 32) | lo;
+  exit:
+    return err;
+}
+
+int marshal_net16(IOStream *io, net16_t x){
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net16(IOStream *io, net16_t *x){
+    int err = 0;
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    return err;
+}
+
+int marshal_net32(IOStream *io, net32_t x){
+    return marshal_bytes(io, &x, sizeof(x));
+}
+
+int unmarshal_net32(IOStream *io, net32_t *x){
+    int err = 0;
+    err = unmarshal_bytes(io, x, sizeof(*x));
+    return err;
+}
+
+int marshal_string(IOStream *io, char *s, uint32_t s_n){
+    int err;
+    //dprintf("> s=%s\n", s);
+    err = marshal_uint32(io, s_n);
+    if(err) goto exit;
+    err = marshal_bytes(io, s, s_n);
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unmarshal_string(IOStream *io, char *s, uint32_t s_n){
+    int err = 0, val_n = 0;
+    //dprintf(">\n");
+    err = unmarshal_uint32(io, &val_n);
+    if(err) goto exit;
+    if(val_n >= s_n){
+        err = -EINVAL;
+        goto exit;
+    }
+    err = unmarshal_bytes(io, s, val_n);
+    if(err) goto exit;
+    s[val_n] = '\0';
+  exit:
+    //dprintf("< err=%d s=%s\n", err, s);
+    return err;
+}
+
+int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){
+    int err = 0, val_n = 0;
+    char *val = NULL;
+    //dprintf(">\n");
+    err = unmarshal_uint32(io, &val_n);
+    if(err) goto exit;
+    val = allocate(val_n + 1);
+    if(!val){
+        err = -ENOMEM;
+        goto exit;
+    }
+    err = unmarshal_bytes(io, val, val_n);
+    if(err) goto exit;
+    val[val_n] = '\0';
+  exit:
+    if(err){
+        if(val) deallocate(val);
+        val = NULL;
+        val_n = 0;
+    }
+    *s = val;
+    if(s_n) *s_n = val_n;
+    //dprintf("< err=%d s=%s\n", err, *s);
+    return err;
+}
diff --git a/tools/xfrd/marshal.h b/tools/xfrd/marshal.h
new file mode 100644 (file)
index 0000000..65e9682
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef _XUTIL_MARSHAL_H_
+#define _XUTIL_MARSHAL_H_
+
+#include "iostream.h"
+
+/** A 16-bit uint in network order, e.g. a port number. */
+typedef uint16_t net16_t;
+
+/** A 32-bit uint in network order, e.g. an IP address. */
+typedef uint32_t net32_t;
+
+extern int marshal_flush(IOStream *io);
+
+extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n);
+extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n);
+
+extern int marshal_uint8(IOStream *io, uint8_t x);
+extern int unmarshal_uint8(IOStream *io, uint8_t *x);
+
+extern int marshal_uint16(IOStream *io, uint16_t x);
+extern int unmarshal_uint16(IOStream *io, uint16_t *x);
+
+extern int marshal_uint32(IOStream *io, uint32_t x);
+extern int unmarshal_uint32(IOStream *io, uint32_t *x);
+
+extern int marshal_int32(IOStream *io, int32_t x);
+extern int unmarshal_int32(IOStream *io, int32_t *x);
+
+extern int marshal_uint64(IOStream *io, uint64_t x);
+extern int unmarshal_uint64(IOStream *io, uint64_t *x);
+
+extern int marshal_net16(IOStream *io, net16_t x);
+extern int unmarshal_net16(IOStream *io, net16_t *x);
+
+extern int marshal_net32(IOStream *io, net32_t x);
+extern int unmarshal_net32(IOStream *io, net32_t *x);
+
+extern int marshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n);
+extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n);
+
+#endif /* ! _XUTIL_MARSHAL_H_ */
diff --git a/tools/xfrd/select.c b/tools/xfrd/select.c
new file mode 100644 (file)
index 0000000..bdaccfe
--- /dev/null
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "select.h"
+
+/** Zero all the file descriptor sets.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_zero(SelectSet *set){
+    set->n = 0;
+    FD_ZERO(&set->rd);
+    FD_ZERO(&set->wr);
+    FD_ZERO(&set->er);
+}
+
+/** Add a file descriptor to the write set.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_add_read(SelectSet *set, int fd){
+    FD_SET(fd, &set->rd);
+    if(fd > set->n) set->n = fd;
+}
+
+/** Add a file descriptor to the write set.
+ *
+ * @param set select set
+ * @param fd file descriptor
+ * @return 0 on success, -1 otherwise
+ */
+void SelectSet_add_write(SelectSet *set, int fd){
+    FD_SET(fd, &set->wr);
+    if(fd > set->n) set->n = fd;
+}
+
+/** Select on file descriptors.
+ *
+ * @param set select set
+ * @param timeout timeout (may be NULL for no timeout)
+ * @return 0 on success, -1 otherwise
+ */
+int SelectSet_select(SelectSet *set, struct timeval *timeout){
+    return select(set->n+1, &set->rd, &set->wr, &set->er, timeout);
+}
diff --git a/tools/xfrd/select.h b/tools/xfrd/select.h
new file mode 100644 (file)
index 0000000..2453f98
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef _XFRD_SELECT_H_
+#define _XFRD_SELECT_H_
+
+/** Set of file descriptors for select.
+ */
+typedef struct SelectSet {
+    int n;
+    fd_set rd, wr, er;
+} SelectSet;
+
+extern void SelectSet_zero(SelectSet *set);
+extern void SelectSet_add_read(SelectSet *set, int fd);
+extern void SelectSet_add_write(SelectSet *set, int fd);
+extern int SelectSet_select(SelectSet *set, struct timeval *timeout);
+
+#endif /* ! _XFRD_SELECT_H_ */
diff --git a/tools/xfrd/sxpr.c b/tools/xfrd/sxpr.c
new file mode 100644 (file)
index 0000000..81fd148
--- /dev/null
@@ -0,0 +1,950 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <stdarg.h>
+#include "sys_string.h"
+#include "lexis.h"
+#include "sys_net.h"
+#include "hash_table.h"
+#include "sxpr.h"
+
+#include <errno.h>
+#undef free
+
+/** @file
+ * General representation of sxprs.
+ * Includes print, equal, and free functions for the sxpr types.
+ *
+ * Zero memory containing an Sxpr will have the value ONONE - this is intentional.
+ * When a function returning an sxpr cannot allocate memory we return ONOMEM.
+ *
+ */
+
+static int atom_print(IOStream *io, Sxpr obj, unsigned flags);
+static int atom_equal(Sxpr x, Sxpr y);
+static void atom_free(Sxpr obj);
+
+static int string_print(IOStream *io, Sxpr obj, unsigned flags);
+static int string_equal(Sxpr x, Sxpr y);
+static void string_free(Sxpr obj);
+
+static int cons_print(IOStream *io, Sxpr obj, unsigned flags);
+static int cons_equal(Sxpr x, Sxpr y);
+static void cons_free(Sxpr obj);
+
+static int null_print(IOStream *io, Sxpr obj, unsigned flags);
+static int none_print(IOStream *io, Sxpr obj, unsigned flags);
+static int int_print(IOStream *io, Sxpr obj, unsigned flags);
+static int bool_print(IOStream *io, Sxpr obj, unsigned flags);
+
+/** Type definitions. */
+static SxprType types[1024] = {
+    [T_NONE]     { type:    T_NONE,     name: "none",       print: none_print      },
+    [T_NULL]     { type:    T_NULL,     name: "null",       print: null_print      },
+    [T_UINT]     { type:    T_UINT,     name: "int",        print: int_print,      },
+    [T_BOOL]     { type:    T_BOOL,     name: "bool",       print: bool_print,     },
+    [T_ATOM]     { type:    T_ATOM,     name: "atom",       print: atom_print,
+                  pointer: TRUE,
+                  free:    atom_free,
+                  equal:   atom_equal,
+                },
+    [T_STRING]   { type:    T_STRING,   name: "string",     print: string_print,
+                  pointer: TRUE,
+                  free:    string_free,
+                  equal:   string_equal,
+                },
+    [T_CONS]     { type:    T_CONS,     name: "cons",       print: cons_print,
+                  pointer: TRUE,
+                  free:    cons_free,
+                  equal:   cons_equal,
+                },
+};
+
+/** Number of entries in the types array. */
+static int type_sup = sizeof(types)/sizeof(types[0]);
+
+/** Get the type definition for a given type code.
+ *
+ * @param ty type code
+ * @return type definition or null
+ */
+SxprType *get_sxpr_type(int ty){
+    if(0 <= ty && ty < type_sup){
+        return types+ty;
+    }
+    return NULL;
+}
+
+/** The default print function.
+ *
+ * @param io stream to print to
+ * @param x sxpr to print
+ * @param flags print flags
+ * @return number of bytes written on success
+ */
+int default_print(IOStream *io, Sxpr x, unsigned flags){
+    return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x));
+}
+
+/** The default equal function.
+ * Uses eq().
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int default_equal(Sxpr x, Sxpr y){
+    return eq(x, y);
+}
+
+/** General sxpr print function.
+ * Prints an sxpr on a stream using the print function for the sxpr type.
+ * Printing is controlled by flags from the PrintFlags enum.
+ * If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr
+ * (for debugging).
+ *
+ * @param io stream to print to
+ * @param x sxpr to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int objprint(IOStream *io, Sxpr x, unsigned flags){
+    SxprType *def = get_sxpr_type(get_type(x));
+    ObjPrintFn *print_fn = (def && def->print ? def->print : default_print);
+    int k = 0;
+    if(!io) return k;
+    if(flags & PRINT_TYPE){
+       k += IOStream_print(io, "%s:", def->name);
+    }
+    k += print_fn(io, x, flags);
+    return k;
+}
+
+/** General sxpr free function.
+ * Frees an sxpr using the free function for its type.
+ * Free functions must recursively free any subsxprs.
+ * If no function is defined then the default is to
+ * free sxprs whose type has pointer true.
+ * Sxprs must not be used after freeing.
+ *
+ * @param x sxpr to free
+ */
+void objfree(Sxpr x){
+    SxprType *def = get_sxpr_type(get_type(x));
+
+    if(def){
+       if(def->free){
+           def->free(x);
+       } else if (def->pointer){
+           hfree(x);
+       }
+    }
+}
+
+/** General sxpr equality function.
+ * Compares x and y using the equal function for x.
+ * Uses default_equal() if x has no equal function.
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int objequal(Sxpr x, Sxpr y){
+    SxprType *def = get_sxpr_type(get_type(x));
+    ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal);
+    return equal_fn(x, y);
+}
+
+/** Search for a key in an alist.
+ * An alist is a list of conses, where the cars
+ * of the conses are the keys. Compares keys using equality.
+ *
+ * @param k key
+ * @param l alist to search
+ * @return first element of l with car k, or ONULL
+ */
+Sxpr assoc(Sxpr k, Sxpr l){
+    for( ; CONSP(l) ; l = CDR(l)){
+        Sxpr x = CAR(l);
+        if(CONSP(x) && objequal(k, CAR(x))){
+            return x;   
+        }
+    }
+    return ONULL;
+}
+
+/** Search for a key in an alist.
+ * An alist is a list of conses, where the cars
+ * of the conses are the keys. Compares keys using eq.
+ *
+ * @param k key
+ * @param l alist to search
+ * @return first element of l with car k, or ONULL
+ */
+Sxpr assocq(Sxpr k, Sxpr l){
+    for( ; CONSP(l); l = CDR(l)){
+        Sxpr x = CAR(l);
+        if(CONSP(x) && eq(k, CAR(x))){
+            return x;
+        }
+    }
+    return ONULL;
+}
+
+/** Add a new key and value to an alist.
+ *
+ * @param k key
+ * @param l value
+ * @param l alist
+ * @return l with the new cell added to the front
+ */
+Sxpr acons(Sxpr k, Sxpr v, Sxpr l){
+    Sxpr x, y;
+    x = cons_new(k, v);
+    if(NOMEMP(x)) return x;
+    y = cons_new(x, l);
+    if(NOMEMP(y)) cons_free_cells(x);
+    return y;
+}
+
+/** Test if a list contains an element.
+ * Uses sxpr equality.
+ *
+ * @param l list
+ * @param x element to look for
+ * @return a tail of l with x as car, or ONULL
+ */
+Sxpr cons_member(Sxpr l, Sxpr x){
+    for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){}
+    return l;
+}
+
+/** Test if a list contains an element satisfying a test.
+ * The test function is called with v and an element of the list.
+ *
+ * @param l list
+ * @param test_fn test function to use
+ * @param v value for first argument to the test
+ * @return a tail of l with car satisfying the test, or 0
+ */
+Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
+    for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ }
+    return l;
+}
+
+/** Test if the elements of list 't' are a subset of the elements
+ * of list 's'. Element order is not significant.
+ *
+ * @param s element list to check subset of
+ * @param t element list to check if is a subset
+ * @return 1 if is a subset, 0 otherwise
+ */
+int cons_subset(Sxpr s, Sxpr t){
+    for( ; CONSP(t); t = CDR(t)){
+       if(!CONSP(cons_member(s, CAR(t)))){
+           return 0;
+       }
+    }
+    return 1;
+}
+
+/** Test if two lists have equal sets of elements.
+ * Element order is not significant.
+ *
+ * @param s list to check
+ * @param t list to check
+ * @return 1 if equal, 0 otherwise
+ */
+int cons_set_equal(Sxpr s, Sxpr t){
+    return cons_subset(s, t) && cons_subset(t, s);
+}
+
+#ifdef USE_GC
+/*============================================================================*/
+/* The functions inside this ifdef are only safe if GC is used.
+ * Otherwise they may leak memory.
+ */
+
+/** Remove an element from a list (GC only).
+ * Uses sxpr equality and removes all instances, even
+ * if there are more than one.
+ *
+ * @param l list to remove elements from
+ * @param x element to remove
+ * @return modified input list
+ */
+Sxpr cons_remove(Sxpr l, Sxpr x){
+    return cons_remove_if(l, eq, x);
+}
+
+/** Remove elements satisfying a test (GC only).
+ * The test function is called with v and an element of the set.
+ *
+ * @param l list to remove elements from
+ * @param test_fn function to use to decide if an element should be removed
+ * @return modified input list
+ */
+Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){
+    Sxpr prev = ONULL, elt, next;
+
+    for(elt = l; CONSP(elt); elt = next){
+        next = CDR(elt);
+        if(test_fn(v, CAR(elt))){
+            if(NULLP(prev)){
+                l = next;
+            } else {
+                CDR(prev) = next;
+            }
+        }
+    }
+    return l;
+}
+
+/** Set the value for a key in an alist (GC only).
+ * If the key is present, changes the value, otherwise
+ * adds a new cell.
+ *
+ * @param k key
+ * @param v value
+ * @param l alist
+ * @return modified or extended list
+ */
+Sxpr setf(Sxpr k, Sxpr v, Sxpr l){
+    Sxpr e = assoc(k, l);
+    if(NULLP(e)){
+        l = acons(k, v, l);
+    } else {
+        CAR(CDR(e)) = v;
+    }
+    return l;
+}
+/*============================================================================*/
+#endif /* USE_GC */
+
+/** Create a new atom with the given name.
+ *
+ * @param name the name
+ * @return new atom
+ */
+Sxpr atom_new(char *name){
+    Sxpr n, obj = ONOMEM;
+
+    n = string_new(name);
+    if(NOMEMP(n)) goto exit;
+    obj = HALLOC(ObjAtom, T_ATOM);
+    if(NOMEMP(obj)) goto exit;
+    OBJ_ATOM(obj)->name = n;
+  exit:
+    return obj;
+}
+
+/** Free an atom.
+ *
+ * @param obj to free
+ */
+void atom_free(Sxpr obj){
+    // Interned atoms are shared, so do not free.
+    if(OBJ_ATOM(obj)->interned) return;
+    objfree(OBJ_ATOM(obj)->name);
+    hfree(obj);
+}
+
+/** Print an atom. Prints the atom name.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes printed
+ */
+int atom_print(IOStream *io, Sxpr obj, unsigned flags){
+    //return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW));
+    return string_print(io, OBJ_ATOM(obj)->name, flags);
+}
+
+/** Atom equality.
+ *
+ * @param x to compare
+ * @param y to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int atom_equal(Sxpr x, Sxpr y){
+    int ok;
+    ok = eq(x, y);
+    if(ok) goto exit;
+    ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name);
+    if(ok) goto exit;
+    ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y);
+  exit:
+    return ok;
+}
+
+/** Get the name of an atom.
+ *
+ * @param obj atom
+ * @return name
+ */
+char * atom_name(Sxpr obj){
+    return string_string(OBJ_ATOM(obj)->name);
+}
+
+/** Get the C string from a string sxpr.
+ *
+ * @param obj string sxpr
+ * @return string
+ */
+char * string_string(Sxpr obj){
+    return OBJ_STRING(obj);
+}
+
+/** Get the length of a string.
+ *
+ * @param obj string
+ * @return length
+ */
+int string_length(Sxpr obj){
+    return strlen(OBJ_STRING(obj));
+}
+
+/** Create a new string. The input string is copied,
+ * and must be null-terminated.
+ *
+ * @param s characters to put in the string
+ * @return new sxpr
+ */
+Sxpr string_new(char *s){
+    int n = (s ? strlen(s) : 0);
+    Sxpr obj;
+    obj = halloc(n+1, T_STRING);
+    if(!NOMEMP(obj)){
+        char *str = OBJ_STRING(obj);
+        strncpy(str, s, n);
+        str[n] = '\0';
+    }
+    return obj;
+}
+
+/** Free a string.
+ *
+ * @param obj to free
+ */
+void string_free(Sxpr obj){
+    hfree(obj);
+}
+
+/** Determine if a string needs escapes when printed
+ * using the given flags.
+ *
+ * @param str string to check
+ * @param flags print flags
+ * @return 1 if needs escapes, 0 otherwise
+ */
+int needs_escapes(char *str, unsigned flags){
+    char *c;
+    int val = 0;
+
+    if(str){
+       for(c=str; *c; c++){
+           if(in_alpha_class(*c)) continue;
+           if(in_decimal_digit_class(*c)) continue;
+           if(in_class(*c, "/._+:@~-")) continue;
+           val = 1;
+           break;
+       }
+    }
+    //printf("\n> val=%d str=|%s|\n", val, str);
+    return val;
+}
+
+/** Print a string to a stream, with escapes if necessary.
+ *
+ * @param io stream to print to
+ * @param str string
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int _string_print(IOStream *io, char *str, unsigned flags){
+    int k = 0;
+    if((flags & PRINT_RAW) || !needs_escapes(str, flags)){
+        k += IOStream_print(io, str);
+    } else {
+       k += IOStream_print(io, "\"");
+       if(str){
+            char *s;
+            for(s = str; *s; s++){
+                if(*s < ' ' || *s >= 127 ){
+                    switch(*s){
+                    case '\a': k += IOStream_print(io, "\\a");  break;
+                    case '\b': k += IOStream_print(io, "\\b");  break;
+                    case '\f': k += IOStream_print(io, "\\f");  break;
+                    case '\n': k += IOStream_print(io, "\\n");  break;
+                    case '\r': k += IOStream_print(io, "\\r");  break;
+                    case '\t': k += IOStream_print(io, "\\t");  break;
+                    case '\v': k += IOStream_print(io, "\\v");  break;
+                    default:
+                        // Octal escape;
+                        k += IOStream_print(io, "\\%o", *s);
+                        break;
+                    }
+                } else if(*s == c_double_quote ||
+                          *s == c_single_quote ||
+                          *s == c_escape){
+                    k += IOStream_print(io, "\\%c", *s);
+                } else {
+                    k+= IOStream_print(io, "%c", *s);
+                }
+            }
+       }
+       k += IOStream_print(io, "\"");
+    }
+    return k;
+}
+
+/** Print a string to a stream, with escapes if necessary.
+ *
+ * @param io stream to print to
+ * @param obj string
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int string_print(IOStream *io, Sxpr obj, unsigned flags){
+    return _string_print(io, OBJ_STRING(obj), flags);
+}
+
+/** Compare an sxpr with a string for equality.
+ *
+ * @param x string to compare with
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int string_equal(Sxpr x, Sxpr y){
+    int ok = 0;
+    ok = eq(x,y);
+    if(ok) goto exit;
+    ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y));
+    if(ok) goto exit;
+    ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y));
+  exit:
+    return ok;
+}
+
+/** Create a new cons cell.
+ * The cell is ONOMEM if either argument is.
+ *
+ * @param car sxpr for the car
+ * @param cdr sxpr for the cdr
+ * @return new cons
+ */
+Sxpr cons_new(Sxpr car, Sxpr cdr){
+    Sxpr obj;
+    if(NOMEMP(car) || NOMEMP(cdr)){
+        obj = ONOMEM;
+    } else {
+        obj = HALLOC(ObjCons, T_CONS);
+        if(!NOMEMP(obj)){
+            ObjCons *z = OBJ_CONS(obj);
+            z->car = car;
+            z->cdr = cdr;
+        }
+    }
+    return obj;
+}
+
+/** Push a new element onto a list.
+ *
+ * @param list list to add to
+ * @param elt element to add
+ * @return 0 if successful, error code otherwise
+ */
+int cons_push(Sxpr *list, Sxpr elt){
+    Sxpr l;
+    l = cons_new(elt, *list);
+    if(NOMEMP(l)) return -ENOMEM;
+    *list = l;
+    return 0;
+}
+
+/** Free a cons. Recursively frees the car and cdr.
+ *
+ * @param obj to free
+ */
+void cons_free(Sxpr obj){
+    Sxpr next;
+    for(; CONSP(obj); obj = next){
+       next = CDR(obj);
+       objfree(CAR(obj));
+       hfree(obj);
+    }
+    if(!NULLP(obj)){
+       objfree(obj);
+    }
+}
+
+/** Free a cons and its cdr cells, but not the car sxprs.
+ * Does nothing if called on something that is not a cons.
+ *
+ * @param obj to free
+ */
+void cons_free_cells(Sxpr obj){
+    Sxpr next;
+    for(; CONSP(obj); obj = next){
+       next = CDR(obj);
+       hfree(obj);
+    }
+}
+
+/** Print a cons.
+ * Prints the cons in list format if the cdrs are conses.
+ * uses pair (dot) format if the last cdr is not a cons (or null).
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+int cons_print(IOStream *io, Sxpr obj, unsigned flags){
+    int first = 1;
+    int k = 0;
+    k += IOStream_print(io, "(");
+    for( ; CONSP(obj) ; obj = CDR(obj)){
+        if(first){ 
+            first = 0;
+        } else {
+            k += IOStream_print(io, " ");
+        }
+        k += objprint(io, CAR(obj), flags);
+    }
+    if(!NULLP(obj)){
+        k += IOStream_print(io, " . ");
+        k += objprint(io, obj, flags);
+    }
+    k += IOStream_print(io, ")");
+    return (IOStream_error(io) ? -1 : k);
+}
+
+/** Compare a cons with another sxpr for equality.
+ * If y is a cons, compares the cars and cdrs recursively.
+ *
+ * @param x cons to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+int cons_equal(Sxpr x, Sxpr y){
+    return CONSP(y) &&
+        objequal(CAR(x), CAR(y)) &&
+        objequal(CDR(x), CDR(y));
+}
+
+/** Return the length of a cons list.
+ *
+ * @param obj list
+ * @return length
+ */
+int cons_length(Sxpr obj){
+    int count = 0;
+    for( ; CONSP(obj); obj = CDR(obj)){
+        count++;
+    }
+    return count;
+}
+
+/** Destructively reverse a cons list in-place.
+ * If the argument is not a cons it is returned unchanged.
+ * 
+ * @param l to reverse
+ * @return reversed list
+ */
+Sxpr nrev(Sxpr l){
+    if(CONSP(l)){
+       // Iterate down the cells in the list making the cdr of
+       // each cell point to the previous cell. The last cell 
+       // is the head of the reversed list.
+       Sxpr prev = ONULL;
+       Sxpr cell = l;
+       Sxpr next;
+
+       while(1){
+           next = CDR(cell);
+           CDR(cell) = prev;
+           if(!CONSP(next)) break;
+           prev = cell;
+           cell = next;
+       }
+       l = cell;
+    }
+    return l;
+}
+
+/** Print the null sxpr.       
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int null_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, "()");
+}
+
+/** Print the `unspecified' sxpr none.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int none_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, "<none>");
+}
+
+/** Print an integer.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int int_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, "%d", OBJ_INT(obj));
+}
+
+/** Print a boolean.
+ *
+ * @param io stream to print to
+ * @param obj to print
+ * @param flags print flags
+ * @return number of bytes written
+ */
+static int bool_print(IOStream *io, Sxpr obj, unsigned flags){
+    return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false));
+}
+
+int sxprp(Sxpr obj, Sxpr name){
+    return CONSP(obj) && objequal(CAR(obj), name);
+}
+
+/** Get the name of an element.
+ * 
+ * @param obj element
+ * @return name
+ */
+Sxpr sxpr_name(Sxpr obj){
+    Sxpr val = ONONE;
+    if(CONSP(obj)){
+        val = CAR(obj);
+    } else if(STRINGP(obj) || ATOMP(obj)){
+        val = obj;
+    }
+    return val;
+}
+
+int sxpr_is(Sxpr obj, char *s){
+    if(ATOMP(obj)) return !strcmp(atom_name(obj), s);
+    if(STRINGP(obj)) return !strcmp(string_string(obj), s);
+    return 0;
+}
+
+int sxpr_elementp(Sxpr obj, Sxpr name){
+    int ok = 0;
+    ok = CONSP(obj) && objequal(CAR(obj), name);
+    return ok;
+}
+
+/** Get the attributes of an sxpr.
+ * 
+ * @param obj sxpr
+ * @return attributes
+ */
+Sxpr sxpr_attributes(Sxpr obj){
+    Sxpr val = ONULL;
+    if(CONSP(obj)){
+        obj = CDR(obj);
+        if(CONSP(obj)){
+            obj = CAR(obj);
+            if(sxprp(obj, intern("@"))){
+                val = CDR(obj);
+            }
+        }
+    }
+    return val;
+}
+
+Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){
+    Sxpr val = ONONE;
+    val = assoc(sxpr_attributes(obj), key);
+    if(CONSP(val) && CONSP(CDR(val))){
+        val = CADR(def);
+    } else {
+        val = def;
+    }
+    return val;
+}
+
+/** Get the children of an sxpr.
+ * 
+ * @param obj sxpr
+ * @return children
+ */
+Sxpr sxpr_children(Sxpr obj){
+    Sxpr val = ONULL;
+    if(CONSP(obj)){
+        val = CDR(obj);
+        if(CONSP(val) && sxprp(CAR(val), intern("@"))){
+            val = CDR(val);
+        }
+    }
+    return val;
+}
+
+Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){
+    Sxpr val = ONONE;
+    Sxpr l;
+    for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){
+        if(sxprp(CAR(l), name)){
+            val = CAR(l);
+            break;
+        }
+    }
+    if(NONEP(val)) val = def;
+    return val;
+}
+
+Sxpr sxpr_child0(Sxpr obj, Sxpr def){
+    Sxpr val = ONONE;
+    Sxpr l = sxpr_children(obj);
+    if(CONSP(l)){
+        val = CAR(l);
+    } else {
+        val = def;
+    }
+    return val;
+}
+
+Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def){
+    Sxpr val = def;
+    Sxpr l;
+    int i;
+    for (i = 0, l = sxpr_children(obj); CONSP(l); i++, l = CDR(l)){
+        if(i == n){
+            val = CAR(l);
+            break;
+        }
+    }
+    return val;
+}
+    
+Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){
+    Sxpr val = ONONE;
+    val = sxpr_child(obj, name, ONONE);
+    if(NONEP(val)){
+        val = def;
+    } else {
+        val = sxpr_child0(val, def);
+    }
+    return val;
+}
+
+/** Table of interned symbols. Indexed by symbol name. */
+static HashTable *symbols = NULL;
+
+/** Hash function for entries in the symbol table.
+ *
+ * @param key to hash
+ * @return hashcode
+ */
+static Hashcode sym_hash_fn(void *key){
+    return hash_string((char*)key);
+}
+
+/** Key equality function for the symbol table.
+ *
+ * @param x to compare
+ * @param y to compare
+ * @return 1 if equal, 0 otherwise
+ */
+static int sym_equal_fn(void *x, void *y){
+    return !strcmp((char*)x, (char*)y);
+}
+
+/** Entry free function for the symbol table.
+ *
+ * @param table the entry is in
+ * @param entry being freed
+ */
+static void sym_free_fn(HashTable *table, HTEntry *entry){
+    if(entry){
+       objfree(((ObjAtom*)entry->value)->name);
+       HTEntry_free(entry);
+    }
+}
+       
+/** Initialize the symbol table.
+ *
+ * @return 0 on sucess, error code otherwise
+ */
+static int init_symbols(void){
+    symbols = HashTable_new(100);
+    if(symbols){
+        symbols->key_hash_fn = sym_hash_fn;
+        symbols->key_equal_fn = sym_equal_fn;
+       symbols->entry_free_fn = sym_free_fn;
+        return 0;
+    }
+    return -1;
+}
+
+/** Cleanup the symbol table. Frees the table and all its symbols.
+ */
+void cleanup_symbols(void){
+    HashTable_free(symbols);
+    symbols = NULL;
+}
+
+/** Get the interned symbol with the given name.
+ * No new symbol is created.
+ *
+ * @return symbol or null
+ */
+Sxpr get_symbol(char *sym){
+    HTEntry *entry;
+    if(!symbols){
+       if(init_symbols()) return ONOMEM;
+       return ONULL;
+    }
+    entry = HashTable_get_entry(symbols, sym);
+    if(entry){
+        return OBJP(T_ATOM, entry->value);
+    } else {
+        return ONULL;
+    }
+}
+
+/** Get the interned symbol with the given name.
+ * Creates a new symbol if necessary.
+ *
+ * @return symbol
+ */
+Sxpr intern(char *sym){
+    Sxpr symbol = get_symbol(sym);
+    if(NULLP(symbol)){
+       if(!symbols) return ONOMEM;
+        symbol = atom_new(sym);
+        if(!NOMEMP(symbol)){
+           OBJ_ATOM(symbol)->interned = TRUE;
+            HashTable_add(symbols, atom_name(symbol), get_ptr(symbol));
+        }
+    }
+    return symbol;
+}
diff --git a/tools/xfrd/sxpr.h b/tools/xfrd/sxpr.h
new file mode 100644 (file)
index 0000000..a5875c7
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of the
+ * License, or  (at your option) any later version. This library is 
+ * distributed in the  hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+#ifndef _XUTIL_SXPR_H_
+#define _XUTIL_SXPR_H_
+
+#include <stdint.h>
+
+#include "hash_table.h"
+#include "iostream.h"
+#include "allocate.h"
+
+/** @file
+ * Definitions for rules and sxprs.
+ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/** Sxpr type. */
+typedef int16_t TypeCode;
+
+/** A typed sxpr handle.*/
+typedef struct Sxpr {
+    /** Sxpr type. */
+    TypeCode type;
+    union {
+       /** Sxpr value. */
+        unsigned long ul;
+       /** Pointer. */
+        void *ptr;
+    } v;
+} Sxpr;
+
+/** Sxpr type to indicate out of memory. */
+#define T_NOMEM      ((TypeCode)-1)
+/** The 'unspecified' sxpr. */
+#define T_NONE       ((TypeCode)0)
+/** The empty list. */
+#define T_NULL       ((TypeCode)1)
+/** Unsigned integer. */
+#define T_UINT       ((TypeCode)2)
+/** A string. */
+#define T_STRING     ((TypeCode)3)
+/** An atom. */
+#define T_ATOM       ((TypeCode)4)
+/** A boolean. */
+#define T_BOOL       ((TypeCode)5)
+
+/** A cons (pair or list). */
+#define T_CONS       ((TypeCode)10)
+
+/** An error. */
+#define T_ERR        ((TypeCode)40)
+
+/** An atom. */
+typedef struct ObjAtom {
+    Sxpr name;
+    Hashcode hashcode;
+    int interned;
+} ObjAtom;
+
+/** A cons (pair). */
+typedef struct ObjCons {
+    Sxpr car;
+    Sxpr cdr;
+} ObjCons;
+
+/** A vector. */
+typedef struct ObjVector {
+    int n;
+    Sxpr data[0];
+} ObjVector;
+
+/** Flags for sxpr printing. */
+enum PrintFlags {
+    PRINT_RAW           = 0x001,
+    PRINT_TYPE          = 0x002,
+    PRINT_PRETTY        = 0x004,
+    PRINT_NUM           = 0x008,
+};
+
+/** An integer sxpr.
+ *
+ * @param ty type
+ * @param val integer value
+ */
+#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }}
+
+/** A pointer sxpr.
+ * If the pointer is non-null, returns an sxpr containing it.
+ * If the pointer is null, returns ONOMEM.
+ *
+ * @param ty type
+ * @param val pointer
+ */
+#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM)
+
+/** Make an integer sxpr containing a pointer.
+ *
+ * @param val pointer
+ */
+#define PTR(val) OBJP(T_UINT, (void*)(val))
+
+/** Make an integer sxpr.
+ * @param x value
+ */
+#define OINT(x)       OBJI(T_UINT,  x)
+
+/** Make an error sxpr.
+ *
+ * @param x value
+ */
+#define OERR(x)       OBJI(T_ERR,   x)
+
+/** Out of memory constant. */
+#define ONOMEM        OBJI(T_NOMEM, 0)
+
+/** The `unspecified' constant. */
+#define ONONE         OBJI(T_NONE,  0)
+
+/** Empty list constant. */
+#define ONULL         OBJI(T_NULL,  0)
+
+/** False constant. */
+#define OFALSE        OBJI(T_BOOL,  0)
+
+/** True constant. */
+#define OTRUE         OBJI(T_BOOL,  1)
+
+/* Recognizers for the various sxpr types.  */
+#define ATOMP(obj)        has_type(obj, T_ATOM)
+#define BOOLP(obj)        has_type(obj, T_BOOL)
+#define CONSP(obj)        has_type(obj, T_CONS)
+#define ERRP(obj)         has_type(obj, T_ERR)
+#define INTP(obj)         has_type(obj, T_UINT)
+#define NOMEMP(obj)       has_type(obj, T_NOMEM)
+#define NONEP(obj)        has_type(obj, T_NONE)
+#define NULLP(obj)        has_type(obj, T_NULL)
+#define STRINGP(obj)      has_type(obj, T_STRING)
+
+#define TRUEP(obj)    get_ul(obj)
+
+/** Convert an sxpr to an unsigned integer. */
+#define OBJ_UINT(x)   get_ul(x)
+/** Convert an sxpr to an integer. */
+#define OBJ_INT(x)    (int)get_ul(x)
+
+/* Conversions of sxprs to their values.
+ * No checking is done.
+ */
+#define OBJ_STRING(x)  ((char*)get_ptr(x))
+#define OBJ_CONS(x)    ((ObjCons*)get_ptr(x))
+#define OBJ_ATOM(x)    ((ObjAtom*)get_ptr(x))
+#define OBJ_SET(x)     ((ObjSet*)get_ptr(x))
+#define CAR(x)         (OBJ_CONS(x)->car)
+#define CDR(x)         (OBJ_CONS(x)->cdr)
+
+#define CAAR(x)        (CAR(CAR(x)))
+#define CADR(x)        (CAR(CDR(x)))
+#define CDAR(x)        (CDR(CAR(x)))
+#define CDDR(x)        (CDR(CDR(x)))
+
+/** Get the integer value from an sxpr.
+ *
+ * @param obj sxpr
+ * @return value
+ */
+static inline unsigned long get_ul(Sxpr obj){
+    return obj.v.ul;
+}
+
+/** Get the pointer value from an sxpr.
+ *
+ * @param obj sxpr
+ * @return value
+ */
+static inline void * get_ptr(Sxpr obj){
+    return obj.v.ptr;
+}
+
+/** Create an sxpr containing a pointer.
+ *
+ * @param type typecode
+ * @param val pointer
+ * @return sxpr
+ */
+static inline Sxpr obj_ptr(TypeCode type, void *val){
+    return (Sxpr){ type: type, v: { ptr: val } };
+}
+
+/** Create an sxpr containing an integer.
+ *
+ * @param type typecode
+ * @param val integer
+ * @return sxpr
+ */
+static inline Sxpr obj_ul(TypeCode type, unsigned long val){
+    return (Sxpr){ type: type, v: { ul: val } };
+}
+
+/** Get the type of an sxpr.
+ *
+ * @param obj sxpr
+ * @return type
+ */
+static inline TypeCode get_type(Sxpr obj){
+    return obj.type;
+}
+
+/** Check the type of an sxpr.
+ *
+ * @param obj sxpr
+ * @param type to check
+ * @return 1 if has the type, 0 otherwise
+ */
+static inline int has_type(Sxpr obj, TypeCode type){
+    return get_type(obj) == type;
+}
+
+/** Compare sxprs for literal equality of type and value.
+ *
+ * @param x sxpr to compare
+ * @param y sxpr to compare
+ * @return 1 if equal, 0 otherwise
+ */
+static inline int eq(Sxpr x, Sxpr y){
+    return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y)));
+}
+
+/** Checked version of CAR
+ *
+ * @param x sxpr
+ * @return CAR if a cons, x otherwise
+ */
+static inline Sxpr car(Sxpr x){
+    return (CONSP(x) ? CAR(x) : x);
+}
+
+/** Checked version of CDR.
+ *
+ * @param x sxpr
+ * @return CDR if a cons, null otherwise
+ */
+static inline Sxpr cdr(Sxpr x){
+    return (CONSP(x) ? CDR(x) : ONULL);
+}
+
+/** Allocate some memory and return an sxpr containing it.
+ * Returns ONOMEM if allocation failed.
+ *
+ * @param n number of bytes to allocate
+ * @param ty typecode
+ * @return sxpr
+ */
+static inline Sxpr halloc(size_t n,  TypeCode ty){
+    return OBJP(ty, allocate(n));
+}
+
+/** Allocate an sxpr containing a pointer to the given type.
+ *
+ * @param ty type (uses sizeof to determine how many bytes to allocate)
+ * @param code typecode
+ * @return sxpr, ONOMEM if allocation failed
+ */
+#define HALLOC(ty, code) halloc(sizeof(ty), code)
+
+typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags);
+typedef int ObjEqualFn(Sxpr obj, Sxpr other);
+typedef void ObjFreeFn(Sxpr obj);
+
+/** An sxpr type definition. */
+typedef struct SxprType {
+    TypeCode type;
+    char *name;
+    int pointer;
+    ObjPrintFn *print;
+    ObjEqualFn *equal;
+    ObjFreeFn *free;
+} SxprType;
+
+
+extern SxprType *get_sxpr_type(int ty);
+
+/** Free the pointer in an sxpr.
+ *
+ * @param x sxpr containing a pointer
+ */
+static inline void hfree(Sxpr x){
+    deallocate(get_ptr(x));
+}
+
+extern int objprint(IOStream *io, Sxpr x, unsigned flags);
+extern int objequal(Sxpr x, Sxpr y);
+extern void objfree(Sxpr x);
+
+extern void cons_free_cells(Sxpr obj);
+extern Sxpr intern(char *s);
+
+extern Sxpr assoc(Sxpr k, Sxpr l);
+extern Sxpr assocq(Sxpr k, Sxpr l);
+extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l);
+extern Sxpr nrev(Sxpr l);
+extern Sxpr cons_member(Sxpr l, Sxpr x);
+extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
+extern int cons_subset(Sxpr s, Sxpr t);
+extern int cons_set_equal(Sxpr s, Sxpr t);
+
+#ifdef USE_GC
+extern Sxpr cons_remove(Sxpr l, Sxpr x);
+extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v);
+#endif
+
+extern Sxpr atom_new(char *name);
+extern char * atom_name(Sxpr obj);
+
+extern Sxpr string_new(char *s);
+extern char * string_string(Sxpr obj);
+extern int string_length(Sxpr obj);
+
+extern Sxpr cons_new(Sxpr car, Sxpr cdr);
+extern int cons_push(Sxpr *list, Sxpr elt);
+extern int cons_length(Sxpr obj);
+
+Sxpr sxpr_name(Sxpr obj);
+int sxpr_is(Sxpr obj, char *s);
+int sxpr_elementp(Sxpr obj, Sxpr name);
+Sxpr sxpr_attributes(Sxpr obj);
+Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def);
+Sxpr sxpr_children(Sxpr obj);
+Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def);
+Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def);
+Sxpr sxpr_child0(Sxpr obj, Sxpr def);
+Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def);
+
+/** Create a new atom.
+ *
+ * @param s atom name
+ * @return new atom
+ */
+static inline Sxpr mkatom(char *s){
+    return atom_new(s);
+}
+
+/** Create a new string sxpr.
+ *
+ * @param s string bytes (copied)
+ * @return new string
+ */
+static inline Sxpr mkstring(char *s){
+    return string_new(s);
+}
+
+/** Create an integer sxpr.
+ *
+ * @param i value
+ * @return sxpr
+ */
+static inline Sxpr mkint(int i){
+    return OBJI(T_UINT, i);
+}
+
+/** Create a boolean sxpr.
+ *
+ * @param b value
+ * @return sxpr
+ */
+static inline Sxpr mkbool(int b){
+    return OBJI(T_BOOL, (b ? 1 : 0));
+}
+
+/* Constants used in parsing and printing. */
+#define k_list_open    "("
+#define c_list_open    '('
+#define k_list_close   ")"
+#define c_list_close   ')'
+#define k_true         "true"
+#define k_false        "false"
+
+#define c_var          '$'
+#define c_escape       '\\'
+#define c_single_quote '\''
+#define c_double_quote '"'
+#define c_string_open  c_double_quote
+#define c_string_close c_double_quote
+#define c_data_open    '['
+#define c_data_close   ']'
+#define c_binary       '*'
+#define c_eval         '!'
+#define c_concat_open  '{'
+#define c_concat_close '}'
+
+#endif /* ! _XUTIL_SXPR_H_ */
diff --git a/tools/xfrd/xdr.c b/tools/xfrd/xdr.c
new file mode 100644 (file)
index 0000000..61dbe44
--- /dev/null
@@ -0,0 +1,316 @@
+#include <errno.h>
+#include "xdr.h"
+
+#define MODULE_NAME "XDR"
+//#define DEBUG 1
+#undef DEBUG
+#include "debug.h"
+
+/** @file
+ * XDR packer/unpacker for elements.
+ *
+ * string -> [T_STRING] [len:u16] <len bytes>
+ * atom   -> [T_ATOM]   [len:u16] <len bytes>
+ * uint   -> [T_UINT]   [value]
+ * cons   -> [T_LIST]   {1 elt}* 0
+ * null   -> [T_NULL]
+ * none   -> [T_NONE]
+ * bool   -> [T_BOOL]   { 0:u8 | 1:u8 }
+ *
+ * types packed as u16.
+ *
+ * So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL]
+ *    ()      -> [T_NULL]
+ */
+
+int pack_bool(IOStream *io, int x){
+    int err=0;
+    //dprintf("> x=%d\n", x);
+    err = IOStream_print(io, "%c", 0xff & x);
+    if(err > 0) err = 0;
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unpack_bool(IOStream *io, int *x){
+    int err = 0;
+    int c;
+    //dprintf(">\n");
+    c = IOStream_getc(io);
+    *x = (c < 0 ? 0 : c);
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    //dprintf("< err=%d x=%d\n", err, *x);
+    return err;
+}
+
+int pack_ushort(IOStream *io, unsigned short x){
+    int err=0;
+    //dprintf("> x=%u\n", x);
+    err = IOStream_print(io, "%c%c",
+                         0xff & (x >>  8),
+                         0xff & (x      ));
+    if(err > 0) err = 0;
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unpack_ushort(IOStream *io, unsigned short *x){
+    int err = 0;
+    int i, c = 0;
+    //dprintf(">\n");
+    *x = 0;
+    for(i = 0; i< 2; i++){
+        c = IOStream_getc(io);
+        if(c < 0) break;
+        *x <<= 8;
+        *x |= (0xff & c);
+    }
+    err = IOStream_error(io);
+
+    if(c < 0 && !err) err = -EIO;
+    //dprintf("< err=%d x=%u\n", err, *x);
+    return err;
+}
+
+int pack_type(IOStream *io, unsigned short x){
+    return pack_ushort(io, x);
+}
+
+int unpack_type(IOStream *io, unsigned short *x){
+    return unpack_ushort(io, x);
+}
+
+int pack_uint(IOStream *io, unsigned int x){
+    int err=0;
+    //dprintf("> x=%u\n", x);
+    err = IOStream_print(io, "%c%c%c%c",
+                         0xff & (x >> 24),
+                         0xff & (x >> 16),
+                         0xff & (x >>  8),
+                         0xff & (x      ));
+    if(err > 0) err = 0;
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unpack_uint(IOStream *io, unsigned int *x){
+    int err = 0;
+    int i, c = 0;
+    //dprintf(">\n");
+    *x = 0;
+    for(i = 0; i< 4; i++){
+        c = IOStream_getc(io);
+        if(c < 0) break;
+        *x <<= 8;
+        *x |= (0xff & c);
+    }
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    //dprintf("< err=%d x=%u\n", err, *x);
+    return err;
+}
+
+int pack_string(IOStream *io, Sxpr x){
+    int err = 0;
+    unsigned short n = 0xffff & string_length(x);
+    char *s = string_string(x);
+    int i;
+    //dprintf("> n=%d s=%s\n", n, s);
+    err = pack_ushort(io, n);
+    if(err) goto exit;
+    for(i = 0; i < n; i++){
+        err = IOStream_print(io, "%c", s[i]);
+        if(err < 0) break;
+    }
+    if(err > 0) err = 0;
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unpack_string(IOStream *io, Sxpr *x){
+    int err;
+    unsigned short n;
+    int i, c = 0;
+    char *s;
+    Sxpr val = ONONE;
+    
+    //dprintf(">\n");
+    err = unpack_ushort(io, &n);
+    if(err) goto exit;
+    val = halloc(n+1, T_STRING);
+    if(NOMEMP(val)){
+        err = -ENOMEM;
+        goto exit;
+    }
+    s = string_string(val);
+    for(i=0; i<n; i++){
+        c = IOStream_getc(io);
+        if(c < 0) break;
+        s[i] = (char)c;
+    }
+    s[n] = '\0';
+  exit:
+    err = IOStream_error(io);
+    if(c < 0 && !err) err = -EIO;
+    if(err){
+        objfree(val);
+        val = ONONE;
+    }
+    *x = val;
+    //IOStream_print(iostdout, "n=%d str=", n); 
+    //objprint(iostdout, *x, 0);
+    //IOStream_print(iostdout, "\n");
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int pack_cons(IOStream *io, Sxpr x){
+    int err = 0;
+    Sxpr l;
+    //dprintf(">\n");
+    for(l = x; CONSP(l); l = CDR(l)){
+        err = pack_bool(io, 1);
+        if(err) goto exit;
+        err = pack_sxpr(io, CAR(l));
+        if(err) goto exit;
+    }
+    err = pack_bool(io, 0);
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unpack_cons(IOStream *io, Sxpr *x){
+    int err = 0;
+    int more = 0;
+    Sxpr u = ONONE, v = ONONE, val = ONULL;
+
+    dprintf(">\n");
+    while(1){
+        err = unpack_bool(io, &more);
+        if(err) goto exit;
+        if(!more){
+            //IOStream_print(iostdout, "unpack_cons 1 val=");
+            ////objprint(iostdout, val, 0);
+            IOStream_print(iostdout, "\n");
+
+            val = nrev(val);
+
+            //IOStream_print(iostdout, "unpack_cons 2 val=");
+            //objprint(iostdout, val, 0);
+            //IOStream_print(iostdout, "\n");
+
+            break;
+        }
+        err = unpack_sxpr(io, &u);
+        if(err) goto exit;
+        v = cons_new(u, val);
+        if(NOMEMP(v)){
+            err = -ENOMEM;
+            objfree(u);
+            goto exit;
+        }
+        val = v;
+    }
+  exit:
+    if(err){
+        objfree(val);
+        val = ONONE;
+    }
+    *x = val;
+    dprintf("< err=%d\n", err);
+    return err;
+}
+    
+int pack_sxpr(IOStream *io, Sxpr x){
+    int err = 0;
+    unsigned short type = get_type(x);
+    //dprintf(">\n");
+    //objprint(iostdout, x, 0);
+    //IOStream_print(iostdout, "\n");
+
+    err = pack_type(io, type);
+    if(err) goto exit;
+    switch(type){
+    case T_NULL:
+        break;
+    case T_NONE:
+        break;
+    case T_BOOL:
+        err = pack_bool(io, get_ul(x));
+        break;
+    case T_CONS:
+        err = pack_cons(io, x);
+        break;
+    case T_ATOM:
+        err = pack_string(io, OBJ_ATOM(x)->name);
+        break;
+    case T_STRING:
+        err = pack_string(io, x);
+        break;
+    case T_UINT:
+        err = pack_uint(io, get_ul(x));
+        break;
+    default:
+        err = -EINVAL;
+        IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
+        break;
+    }
+  exit:
+    //dprintf("< err=%d\n", err);
+    return err;
+}
+
+int unpack_sxpr(IOStream *io, Sxpr *x){
+    int err = 0;
+    unsigned short type;
+    unsigned int u;
+    Sxpr val = ONONE, y;
+
+    //dprintf(">\n");
+    err = unpack_type(io, &type);
+    if(err) goto exit;
+    switch(type){
+    case T_NULL:
+        val = ONULL;
+        break;
+    case T_NONE:
+        val = ONONE;
+        break;
+    case T_CONS:
+        err = unpack_cons(io, &val);
+        break;
+    case T_BOOL:
+        err = unpack_bool(io, &u);
+        if(err) goto exit;
+        val = (u ? OTRUE : OFALSE);
+        break;
+    case T_ATOM:
+        err = unpack_string(io, &y);
+        if(err) goto exit;
+        val = intern(string_string(y));
+        objfree(y);
+        break;
+    case T_STRING:
+        err = unpack_string(io, &val);
+        break;
+    case T_UINT:
+        err = unpack_uint(io, &u);
+        if(err) goto exit;
+        val = OBJI(type, u);
+        break;
+    default:
+        err = -EINVAL;
+        IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type);
+        break;
+    }
+  exit:
+    *x = (err ? ONONE : val);
+    //IOStream_print(iostdout, "sxpr="); 
+    //objprint(iostdout, *x, 0);
+    //IOStream_print(iostdout, "\n");
+    //dprintf("< err=%d\n", err);
+    return err;
+}
diff --git a/tools/xfrd/xdr.h b/tools/xfrd/xdr.h
new file mode 100644 (file)
index 0000000..793cd34
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _XUTIL_XDR_H_
+#define _XUTIL_XDR_H_
+#include "iostream.h"
+#include "sxpr.h"
+
+int pack_type(IOStream *io, unsigned short x);
+
+int unpack_type(IOStream *io, unsigned short *x);
+
+int pack_bool(IOStream *io, int x);
+
+int unpack_bool(IOStream *io, int *x);
+
+int pack_uint(IOStream *out, unsigned int x);
+
+int unpack_uint(IOStream *in, unsigned int *x);
+
+int pack_string(IOStream *out, Sxpr x);
+
+int unpack_string(IOStream *in, Sxpr *x);
+
+int pack_cons(IOStream *out, Sxpr x);
+
+int unpack_cons(IOStream *in, Sxpr *x);
+
+int pack_sxpr(IOStream *out, Sxpr x);
+
+int unpack_sxpr(IOStream *in, Sxpr *x);
+
+#endif /* _XUTIL_XDR_H_ */
diff --git a/tools/xfrd/xen_domain.c b/tools/xfrd/xen_domain.c
new file mode 100644 (file)
index 0000000..5c5f7c5
--- /dev/null
@@ -0,0 +1,90 @@
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#ifndef _XEN_XFR_STUB_
+#include "dom0_defs.h"
+#include "mem_defs.h"
+#endif
+
+#include "xen_domain.h"
+#include "marshal.h"
+#include "xdr.h"
+
+#define MODULE_NAME "XFRD"
+#define DEBUG 1
+#include "debug.h"
+
+/** Write domain state.
+ *
+ * At some point during this the domain is suspended, and then there's no way back.
+ * Even if something later goes wrong we can't restart the domain.
+ */
+int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n){
+    int err = 0;
+    char buf[1024];
+    int n, k, d, buf_n;
+    dprintf("> dom=%d\n", dom);
+#ifdef _XEN_XFR_STUB_
+    err = marshal_uint32(io, dom);
+    if(err) goto exit;
+    err = marshal_string(io, vmconfig, vmconfig_n);
+    if(err) goto exit;
+    n = 32 * 1024 * 1024;
+    buf_n = sizeof(buf);
+    err = marshal_uint32(io, n);
+    for(k = 0; k < n; k += d){
+        d = n - k;
+        if(d > buf_n) d = buf_n;
+        err = marshal_bytes(io, buf, d);
+        if(err) goto exit;
+        //dprintf("> k=%d n=%d\n", k, n);
+    }
+    
+  exit:
+#else 
+#endif   
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Receive domain state.
+ * Create a new domain and store the received state into it.
+ */
+int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n){
+    int err = 0;
+    char buf[1024];
+    int n, k, d, buf_n;
+    dprintf(">\n");
+#ifdef _XEN_XFR_STUB_
+    err = unmarshal_uint32(io, dom);
+    if(err) goto exit;
+    err = unmarshal_new_string(io, vmconfig, vmconfig_n);
+    if(err) goto exit;
+    err = unmarshal_uint32(io, &n);
+    buf_n = sizeof(buf);
+    for(k = 0; k < n; k += d){
+        d = n - k;
+        if(d > buf_n) d = buf_n;
+        err = unmarshal_bytes(io, buf, d);
+        if(err) goto exit;
+        //dprintf("> k=%d n=%d\n", k, n);
+    }
+  exit:
+#else    
+#endif   
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Configure a new domain. Talk to xend. Use libcurl?
+ */
+int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){
+    int err = 0;
+    dprintf(">\n");
+#ifdef _XEN_XFR_STUB_
+#else    
+#endif   
+    dprintf("< err=%d\n", err);
+    return err;
+}
diff --git a/tools/xfrd/xen_domain.h b/tools/xfrd/xen_domain.h
new file mode 100644 (file)
index 0000000..4618c64
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _XFRD_XEN_DOMAIN_H_
+#define _XFRD_XEN_DOMAIN_H_
+#include <sys/types.h>
+#include <iostream.h>
+#include "connection.h"
+
+/** Define to use stubs. Undefine to use Xen ops. */
+//#define _XEN_XFR_STUB_
+
+extern int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n);
+extern int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n);
+
+
+extern int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n);
+#endif
diff --git a/tools/xfrd/xfrd.c b/tools/xfrd/xfrd.c
new file mode 100644 (file)
index 0000000..1836231
--- /dev/null
@@ -0,0 +1,1127 @@
+/** @file
+ * XFRD - Domain Transfer Daemon for Xen.
+ *
+ * The xfrd is forked by xend to transfer a vm to a remote system.
+ *
+ * The vm is suspended, then its state and memory are transferred to the remote system.
+ * The remote system attempts to create a vm and copy the transferred state and memory into it,
+ * finally resuming the vm. If all is OK the vm ends up running on the remote
+ * system and is removed from the originating system. If the transfer does not complete
+ * successfully the originating system attempts to resume the vm.
+ * The children exit when the transfer completes.
+ *
+ * @author Mike Wray <mike.wray@hpl.hp.com>
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+
+#include "allocate.h"
+#include "file_stream.h"
+#include "string_stream.h"
+#include "lzi_stream.h"
+#include "sys_net.h"
+#include "sys_string.h"
+
+#include "xdr.h"
+#include "enum.h"
+#include "xfrd.h"
+
+#include "xen_domain.h"
+
+#include "connection.h"
+#include "select.h"
+
+#define MODULE_NAME "XFRD"
+#define DEBUG 1
+#include "debug.h"
+
+/*
+sender:
+        xend connects to xfrd and writes migrate message
+        xend writes domain config to xfrd
+
+        xfrd forks
+
+        xfrd connects to peer
+        xfrd sends hello, reads response
+        xfrd sends domain
+        xfrd reads response
+        reports progress/status to xend
+
+        xend reads xfrd for progress/status, disconnects
+        If ok, destroys domain.
+        If not ok, unpauses domain.
+
+receiver:
+        xfrd accepts connection on inbound port
+        xfrd forks and accepts connection
+        xfrd receives hello, writes response
+        xfrd receives domain
+        xfrd connects to xend, configures new domain
+        xfrd writes status back to peer, child exits
+
+
+        (xfr.hello <major> <minor>)
+        (xfr.err <code> <reason>)
+
+        xend->xfrd (xfr.migrate  <domain> <vmconfig> <host> <port>)
+                   (xfr.save <domain> <vmconfig> <file>)
+        xfrd->xend (xfr.suspend <domain>)
+        xfrd->xend (xfr.progress <percent> <rate: kb/s>)
+        xfrd->xend (xfr.err <code> <reason>) | (xfr.ok <domain>)
+        xfrd->xfrd (xfr.xfr <domain>)
+        xfrd->xfrd (xfr.err <code>) | (xfr.ok <domain>)
+
+        xfrd->xend (xfr.configure <domain> <vmconfig>)
+ */
+
+Sxpr oxfr_configure; // (xfr.configure <vmid> <vmconfig>)
+Sxpr oxfr_err;       // (xfr.err <code>)
+Sxpr oxfr_hello;     // (xfr.hello <major> <minor>)
+Sxpr oxfr_migrate;   // (xfr.migrate <vmid> <vmconfig> <host> <port>)
+Sxpr oxfr_ok;        // (xfr.ok <value>)
+Sxpr oxfr_progress;  // (xfr.progress <percent> <rate: kb/s>)
+Sxpr oxfr_save;      // (xfr.save <vmid> <vmconfig> <file>)
+Sxpr oxfr_suspend;   // (xfr.suspend <vmid>)
+Sxpr oxfr_xfr;       // (xfr.xfr <vmid>)
+
+void xfr_init(void){
+    oxfr_configure = intern("xfr.configure");
+    oxfr_err       = intern("xfr.err");
+    oxfr_hello     = intern("xfr.hello");
+    oxfr_migrate   = intern("xfr.migrate");
+    oxfr_ok        = intern("xfr.ok");
+    oxfr_progress  = intern("xfr.progress");
+    oxfr_save      = intern("xfr.save");
+    oxfr_suspend   = intern("xfr.suspend");
+    oxfr_xfr       = intern("xfr.xfr");
+}
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define PROGRAM      "xfrd"
+
+#define OPT_PORT     'P'
+#define KEY_PORT     "port"
+#define DOC_PORT     "<port>\n\txfr port (as a number or service name)"
+
+#define OPT_COMPRESS 'Z'
+#define KEY_COMPRESS "compress"
+#define DOC_COMPRESS "\n\tuse compression for migration"
+
+#define OPT_HELP     'h'
+#define KEY_HELP     "help"
+#define DOC_HELP     "\n\tprint help"
+
+#define OPT_VERSION  'v'
+#define KEY_VERSION  "version"
+#define DOC_VERSION  "\n\tprint version"
+
+#define OPT_VERBOSE  'V'
+#define KEY_VERBOSE  "verbose"
+#define DOC_VERBOSE  "\n\tverbose flag"
+
+/** Print a usage message.
+ * Prints to stdout if err is zero, and exits with 0.
+ * Prints to stderr if err is non-zero, and exits with 1.
+ */
+void usage(int err){
+    FILE *out = (err ? stderr : stdout);
+
+    fprintf(out, "Usage: %s [options]\n", PROGRAM);
+    fprintf(out, "-%c, --%s %s\n", OPT_PORT,     KEY_PORT,     DOC_PORT);
+    fprintf(out, "-%c, --%s %s\n", OPT_COMPRESS, KEY_COMPRESS, DOC_COMPRESS);
+    fprintf(out, "-%c, --%s %s\n", OPT_VERBOSE,  KEY_VERBOSE,  DOC_VERBOSE);
+    fprintf(out, "-%c, --%s %s\n", OPT_VERSION,  KEY_VERSION,  DOC_VERSION);
+    fprintf(out, "-%c, --%s %s\n", OPT_HELP,     KEY_HELP,     DOC_HELP);
+    exit(err ? 1 : 0);
+}
+
+/** Short options. Options followed by ':' take an argument. */
+static char *short_opts = (char[]){
+    OPT_PORT,     ':',
+    OPT_COMPRESS,
+    OPT_HELP,
+    OPT_VERSION,
+    OPT_VERBOSE,
+    0 };
+
+/** Long options. */
+static struct option const long_opts[] = {
+    { KEY_PORT,     required_argument, NULL, OPT_PORT     },
+    { KEY_COMPRESS, no_argument,       NULL, OPT_COMPRESS },
+    { KEY_HELP,     no_argument,       NULL, OPT_HELP     },
+    { KEY_VERSION,  no_argument,       NULL, OPT_VERSION  },
+    { KEY_VERBOSE,  no_argument,       NULL, OPT_VERBOSE  },
+    { NULL,         0,                 NULL, 0            }
+};
+
+typedef struct Args {
+    int bufsize;
+    unsigned long port;
+    int verbose;
+    int compress;
+} Args;
+
+/** Transfer states. */
+enum {
+    XFR_INIT,
+    XFR_HELLO,
+    XFR_STATE,
+    XFR_RUN,
+    XFR_FAIL,
+    XFR_DONE,
+    XFR_MAX
+};
+
+/** Initialize an array element for a constant to its string name. */
+#define VALDEF(val) { val, #val }
+
+/** Names for the transfer states. */
+static EnumDef xfr_states[] = {
+    VALDEF(XFR_INIT),
+    VALDEF(XFR_HELLO),
+    VALDEF(XFR_STATE),
+    VALDEF(XFR_RUN),
+    VALDEF(XFR_FAIL),
+    VALDEF(XFR_DONE),
+    { 0, NULL }
+};
+    
+
+/** State machine for transfer. */
+typedef struct XfrState {
+    /** Current state. */
+    int state;
+    /** Error codes for the states. */
+    int state_err[XFR_MAX];
+    /** First error. */
+    int err;
+    /** State when first error happened. */
+    int err_state;
+
+    uint32_t vmid;
+    char* vmconfig;
+    int vmconfig_n;
+    unsigned long xfr_port;
+    char *xfr_host;
+    uint32_t vmid_new;
+} XfrState;
+
+/** Get the name of a transfer state.
+ *
+ * @param s state
+ * @return name
+ */
+char * xfr_state_name(int s){
+    return enum_val_to_name(s, xfr_states);
+}
+
+/** Set the state of a transfer.
+ *
+ * @param s transfer
+ * @param state state
+ * @return state
+ */
+int XfrState_set_state(XfrState *s, int state){
+    s->state = state;
+    return s->state;
+}
+
+/** Get the state of a transfer.
+ *
+ * @param s transfer
+ * @return state
+ */
+int XfrState_get_state(XfrState *s){
+    return s->state;
+}
+
+/** Set an error in the current state.
+ * Does nothing if an error is already set.
+ *
+ * @param s transfer
+ * @param err error
+ * @return error
+ */
+int XfrState_set_err(XfrState *s, int err){
+    if(!s->state_err[s->state]){
+        s->state_err[s->state] = err;
+    }
+    if(!s->err){
+        s->err = err;
+        s->err_state = s->state;
+    }
+    return err;
+}
+
+/** Get the error in the current state.
+ *
+ * @param s transfer
+ * @return error
+ */
+int XfrState_get_err(XfrState *s){
+    return s->state_err[s->state];
+}
+
+/** Get the first error of a transfer.
+ *
+ * @param s transfer
+ * @return error
+ */
+int XfrState_first_err(XfrState *s){
+    return s->err;
+}
+
+/** Get the state a transfer was in when it had its first error.
+ *
+ * @param s transfer
+ * @return error state
+ */
+int XfrState_first_err_state(XfrState *s){
+    return s->err_state;
+}
+
+/** Xfrd arguments. */
+static Args _args = {};
+
+/** Xfrd arguments. */
+static Args *args = &_args;
+
+/** Set xfrd default arguments.
+ *
+ * @param args arguments to set
+ */
+void set_defaults(Args *args){
+    args->compress = FALSE;
+    args->bufsize = 128 * 1024;
+    args->port = htons(XFRD_PORT);
+}
+
+int stringof(Sxpr exp, char **s){
+    int err = 0;
+    dprintf(">\n");
+    objprint(iostdout, exp, PRINT_TYPE); IOStream_print(iostdout, "\n");
+    if(ATOMP(exp)){
+        *s = atom_name(exp);
+    } else if(STRINGP(exp)){
+        *s = string_string(exp);
+    } else {
+        err = -EINVAL;
+        *s = NULL;
+    }
+    dprintf("< err=%d s=%s\n", err, *s);
+    return err;
+}
+
+int intof(Sxpr exp, int *v){
+    int err = 0;
+    char *s;
+    unsigned long l;
+    dprintf(">\n");
+    objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+    if(INTP(exp)){
+        *v = OBJ_INT(exp);
+    } else {
+        err = stringof(exp, &s);
+        if(err) goto exit;
+        err = convert_atoul(s, &l);
+        *v = (int)l;
+    }
+ exit:
+    dprintf("< err=%d v=%d\n", err, *v);
+    return err;
+}
+
+int addrof(Sxpr exp, uint32_t *v){
+    char *h;
+    unsigned long a;
+    int err = 0;
+    dprintf(">\n");
+    objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+    err = stringof(exp, &h);
+    if(err) goto exit;
+    if(get_host_address(h, &a)){
+        *v = a;
+    } else {
+        err = -EINVAL;
+    }
+  exit:
+    dprintf("< err=%d v=%x\n", err, *v);
+    return err;
+}
+
+int portof(Sxpr exp, uint16_t *v){
+    char *s;
+    int err = 0;
+    dprintf(">\n");
+    objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n");
+    if(INTP(exp)){
+        *v = get_ul(exp);
+        *v = htons(*v);
+    } else {
+        unsigned long p;
+        err = stringof(exp, &s);
+        if(err) goto exit;
+        if(get_service_port(s, &p)){
+            *v = p;
+        } else {
+            err = -EINVAL;
+        }
+    }
+  exit:
+    dprintf("< err=%d v=%u\n", err, *v);
+    return err;
+}
+
+static inline struct in_addr inaddr(uint32_t addr){
+    return (struct in_addr){ .s_addr = addr };
+}
+
+time_t stats(time_t t0, uint64_t offset, uint64_t memory, float *percent, float *rate){
+    time_t t1 = time(NULL);
+    *percent = (offset * 100.0f) / memory;
+    t1 = time(NULL) - t0;
+    *rate = (t1 ?  offset/(t1 * 1024.0f) : 0.0f);
+    return t1;
+}
+
+/** Notify success or error.
+ *
+ * @param conn connection
+ * @param errcode error code
+ * @return 0 on success, error code otherwise
+ */
+int xfr_error(Conn *conn, int errcode){
+    int err = 0;
+    if(!conn->out) return -ENOTCONN;
+    err = pack_type(conn->out, T_CONS);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, oxfr_err);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, mkint(errcode));
+    if(err) goto exit;
+    err = pack_bool(conn->out, 0);
+  exit:
+    return err;
+}
+
+/** Read a response message - error or ok.
+ *
+ * @param conn connection
+ * @return 0 on success, error code otherwise
+ */
+int xfr_response(Conn *conn){
+    int err;
+    Sxpr sxpr;
+
+    dprintf(">\n");
+    if(!conn->out) return -ENOTCONN;
+    err = unpack_sxpr(conn->in, &sxpr);
+    if(err) goto exit;
+    if(sxpr_elementp(sxpr, oxfr_err)){
+        int errcode;
+        err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode);
+        if(err) goto exit;
+        err = errcode;
+    }
+  exit:
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Get the initial hello message and check the protocol version.
+ * It is an error to receive anything other than a hello message
+ * with the correct protocol version.
+ *
+ * @param conn connection
+ * @return 0 on success, error code otherwise
+ */
+int xfr_hello(Conn *conn){
+    int err;
+    uint32_t major = XFR_PROTO_MAJOR, minor = XFR_PROTO_MINOR;
+    uint32_t hello_major, hello_minor;
+    Sxpr sxpr;
+    if(!conn->in) return -ENOTCONN;
+    dprintf(">\n");
+    err = unpack_sxpr(conn->in, &sxpr);
+    if(err) goto exit;
+    if(!sxpr_elementp(sxpr, oxfr_hello)){
+        dprintf("> sxpr_elementp test failed\n");
+        err = -EINVAL;
+        goto exit;
+    }
+    err = intof(sxpr_childN(sxpr, 0, ONONE), &hello_major);
+    if(err) goto exit;
+    err = intof(sxpr_childN(sxpr, 1, ONONE), &hello_minor);
+    if(err) goto exit;
+    if(hello_major != major || hello_minor != minor){
+        eprintf("> Wanted protocol version %d.%d, got %d.%d",
+                major, minor, hello_major, hello_minor);
+        err = -EINVAL;
+        goto exit;
+    }
+  exit:
+    xfr_error(conn, err);
+    if(err){
+        eprintf("> Hello failed: %d\n", err);
+    }
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Send the initial hello message.
+ *
+ * @param conn connection
+ * @param msg  message
+ * @return 0 on success, error code otherwise
+ */
+int xfr_send_hello(Conn *conn){
+    int err = 0;
+    dprintf(">\n");
+    err = pack_type(conn->out, T_CONS);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, oxfr_hello);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, mkint(XFR_PROTO_MAJOR));
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, mkint(XFR_PROTO_MINOR));
+    if(err) goto exit;
+    err = pack_bool(conn->out, 0);
+    IOStream_flush(conn->out);
+    dprintf("> xfr_response...\n");
+    err = xfr_response(conn);
+  exit:
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+int xfr_send_xfr(Conn *conn, uint32_t vmid){
+    int err;
+    err = pack_type(conn->out, T_CONS);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, oxfr_xfr);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, mkint(vmid));
+    if(err) goto exit;
+    err = pack_bool(conn->out, 0);
+    if(err) goto exit;
+  exit:
+    return err;
+}
+
+int xfr_send_ok(Conn *conn, uint32_t vmid){
+    int err = 0;
+    err = pack_type(conn->out, T_CONS);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, oxfr_ok);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, mkint(vmid));
+    if(err) goto exit;
+    err = pack_bool(conn->out, 0);
+  exit:
+    return err;
+}
+
+int xfr_send_suspend(Conn *conn, uint32_t vmid){
+    int err = 0;
+
+    err = pack_type(conn->out, T_CONS);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, oxfr_suspend);
+    if(err) goto exit;
+    err = pack_bool(conn->out, 1);
+    if(err) goto exit;
+    err = pack_sxpr(conn->out, mkint(vmid));
+    if(err) goto exit;
+    err = pack_bool(conn->out, 0);
+  exit:
+    return err;
+}
+
+/** Get vm state. Send transfer message.
+ *
+ * @param peer connection
+ * @param msg  message
+ * @return 0 on success, error code otherwise
+ */
+int xfr_send_state(XfrState *state, Conn *xend, Conn *peer){
+    int err = 0;
+    Sxpr sxpr;
+    
+    dprintf(">\n");
+    XfrState_set_state(state, XFR_STATE);
+    // Send xfr message and the domain state.
+    err = xfr_send_xfr(peer, state->vmid);
+    if(err) goto exit;
+    err = xen_domain_snd(xend, peer->out, state->vmid, state->vmconfig, state->vmconfig_n);
+    if(err) goto exit;
+    IOStream_flush(peer->out);
+    // Read the response from the peer.
+    err = unpack_sxpr(peer->in, &sxpr);
+    if(err) goto exit;
+    if(sxpr_elementp(sxpr, oxfr_err)){
+        // Error.
+        int errcode;
+        err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode);
+        if(!err) err = errcode;
+    } else if(sxpr_elementp(sxpr, oxfr_ok)){
+        // Ok - get the new domain id.
+        err = intof(sxpr_childN(sxpr, 0, ONONE), &state->vmid_new);
+        xfr_error(peer, err);
+    } else {
+        // Anything else is invalid. But it may be too late.
+        err = -EINVAL;
+        xfr_error(peer, err);
+    }
+  exit:
+    XfrState_set_err(state, err);
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Finish the transfer.
+ */
+int xfr_send_done(XfrState *state, Conn *xend){
+    int err = 0;
+    int first_err = 0;
+
+    first_err = XfrState_first_err(state);
+    if(first_err){
+        XfrState_set_state(state, XFR_FAIL);
+    } else {
+        XfrState_set_state(state, XFR_DONE);
+    }
+    if(first_err){
+        err = xfr_error(xend, first_err);
+    } else {
+        // Report new domain id to xend.
+        err = xfr_send_ok(xend, state->vmid_new);
+    }  
+
+    XfrState_set_err(state, err);
+    if(XfrState_first_err(state)){
+        int s, serr;
+
+        wprintf("> Transfer errors:\n");
+        for(s = 0; s < XFR_MAX; s++){
+            serr = state->state_err[s];
+            if(!serr) continue;
+            wprintf("> state=%-12s err=%d\n", xfr_state_name(s), serr);
+        }
+    } else {
+        wprintf("> Transfer OK\n");
+    }
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Migrate a vm to another node.
+ *
+ * @param xend connection
+ * @return 0 on success, error code otherwise
+ */
+int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t port){
+    int err = 0;
+    Conn _peer = {}, *peer = &_peer;
+    int flags = 0;
+    struct in_addr xfr_addr;
+    uint16_t xfr_port;
+    time_t t0 = time(NULL), t1;
+
+    dprintf(">\n");
+    flags |= CONN_NOBUFFER;
+    if(args->compress){
+        flags |= CONN_WRITE_COMPRESS;
+    }
+    xfr_addr.s_addr = addr;
+    xfr_port = port;
+    if(!xfr_port) xfr_port = htons(XFRD_PORT);
+    dprintf("> Xfr vmid=%u\n", state->vmid);
+    dprintf("> Xfr xfr_addr=%s:%d\n", inet_ntoa(xfr_addr), ntohs(xfr_port));
+    err = Conn_connect(peer, flags, xfr_addr, xfr_port);
+    if(err) goto exit;
+    printf("\n");
+    XfrState_set_state(state, XFR_HELLO);
+    // Send hello message.
+    err = xfr_send_hello(peer);
+    if(err) goto exit;
+    printf("\n");
+    // Send vm state.
+    err = xfr_send_state(state, xend, peer);
+    if(err) goto exit;
+    if(args->compress){
+        IOStream *zio = peer->out;
+        int plain_bytes = lzi_stream_plain_bytes(zio);
+        int comp_bytes = lzi_stream_comp_bytes(zio);
+        float ratio = lzi_stream_ratio(zio);
+        dprintf("> Compression: plain %d bytes, compressed %d bytes, ratio %3.2f\n",
+                plain_bytes, comp_bytes, ratio);
+    }
+    printf("\n");
+  exit:
+    dprintf("> err=%d\n", err);
+    if(err && !XfrState_get_err(state)){
+        XfrState_set_err(state, err);
+    }
+    Conn_close(peer);
+    if(!err){
+        t1 = time(NULL) - t0;
+        dprintf("> Transfer complete in %lu seconds\n", t1);
+    }
+    dprintf("> done err=%d, notifying xend...\n", err);
+    xfr_send_done(state, xend);
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Save a vm to file.
+ */
+int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){
+    int err = 0;
+    IOStream *io = NULL;
+
+    io = file_stream_fopen(file, "wb");
+    if(!io){
+        err = -EIO;
+        goto exit;
+    }
+    err = xen_domain_snd(xend, io, state->vmid, state->vmconfig, state->vmconfig_n);
+  exit:
+    if(io){
+        IOStream_close(io);
+        IOStream_free(io);
+    }
+    return err;
+}
+
+/** Accept the transfer of a vm from another node.
+ *
+ * @param peer connection
+ * @param msg  message
+ * @return 0 on success, error code otherwise
+ */
+int xfr_recv(Args *args, XfrState *state, Conn *peer){
+    int err = 0;
+    time_t t0 = time(NULL), t1;
+
+    dprintf(">\n");
+    err = xen_domain_rcv(peer->in, &state->vmid_new, &state->vmconfig, &state->vmconfig_n);
+    if(err) goto exit;
+
+    err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n);
+    if(err) goto exit;
+
+    // Report new domain id to peer.
+    err = xfr_send_ok(peer, state->vmid_new);
+    if(err) goto exit;
+    // Get the final ok.
+    err = xfr_response(peer);
+  exit:
+    if(!err){
+        t1 = time(NULL) - t0;
+        dprintf("> Transfer complete in %lu seconds\n", t1);
+    }
+    if(err){
+        xfr_error(peer, err);
+    }
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Listen for a hello followed by a service request.
+ * The request can be from the local xend or from xfrd on another node.
+ *
+ * @param peersock socket
+ * @param peer_in peer address
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){
+    int err = 0;
+    Sxpr sxpr;
+    Conn _conn = {}, *conn = &_conn;
+    int flags = CONN_NOBUFFER;
+
+    dprintf(">\n");
+    err = Conn_init(conn, flags, peersock, peer_in);
+    if(err) goto exit;
+    err = xfr_hello(conn);
+    if(err) goto exit;
+    err = unpack_sxpr(conn->in, &sxpr);
+    if(err) goto exit;
+    if(sxpr_elementp(sxpr, oxfr_migrate)){
+        // Migrate message from xend.
+        uint32_t addr;
+        uint16_t port;
+        XfrState _state = {}, *state = &_state;
+        int n = 0;
+
+        dprintf("> xfr.migrate\n");
+        err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid);
+        if(err) goto exit;
+        err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig);
+        if(err) goto exit;
+        state->vmconfig_n = strlen(state->vmconfig);
+        err = addrof(sxpr_childN(sxpr, n++, ONONE), &addr);
+        if(err) goto exit;
+        err = portof(sxpr_childN(sxpr, n++, ONONE), &port);
+        if(err) goto exit;
+        err = xfr_send(args, state, conn, addr, port);
+
+    } else if(sxpr_elementp(sxpr, oxfr_save)){
+        // Save message from xend.
+        char *file;
+        XfrState _state = {}, *state = &_state;
+        int n = 0;
+
+        dprintf("> xfr.save\n");
+        err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid);
+        if(err) goto exit;
+        err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig);
+        if(err) goto exit;
+        state->vmconfig_n = strlen(state->vmconfig);
+        err = stringof(sxpr_childN(sxpr, n++, ONONE), &file);
+        if(err) goto exit;
+        err = xfr_save(args, state, conn, file);
+
+    } else if(sxpr_elementp(sxpr, oxfr_xfr)){
+        // Xfr message from peer xfrd.
+        XfrState _state = {}, *state = &_state;
+        int n = 0;
+
+        dprintf("> xfr.xfr\n");
+        err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid);
+        if(err) goto exit;
+        err = xfr_recv(args, state, conn);
+
+    } else{
+        // Anything else is invalid.
+        err = -EINVAL;
+        dprintf("> Invalid message: ");
+        objprint(iostderr, sxpr, 0);
+        IOStream_print(iostderr, "\n");
+        xfr_error(conn, err);
+    }
+  exit:
+    Conn_close(conn);
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Accept an incoming connection.
+ *
+ * @param sock tcp socket
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_accept(Args *args, int sock){
+    struct sockaddr_in peer_in;
+    struct sockaddr *peer = (struct sockaddr *)&peer_in;
+    socklen_t peer_n = sizeof(peer_in);
+    int peersock;
+    pid_t pid;
+    int err = 0;
+    
+    dprintf(">\n");
+    dprintf("> accept...\n");
+    peersock = accept(sock, peer, &peer_n);
+    dprintf("> accept=%d\n", peersock);
+    if(peersock < 0){
+        perror("accept");
+        err = -errno;
+        goto exit;
+    }
+    iprintf("> Accepted connection from %s:%d\n",
+            inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+    pid = fork();
+    if(pid > 0){
+        // Parent, fork succeeded.
+        iprintf("> Forked child pid=%d\n", pid);
+        close(peersock);
+    } else if (pid < 0){
+        // Parent, fork failed.
+        perror("fork");
+        close(peersock);
+    } else {
+        // Child.
+        iprintf("> Xfr service for %s:%d\n",
+                inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port));
+        err = xfrd_service(args, peersock, peer_in);
+        iprintf("> Xfr service err=%d\n", err);
+        shutdown(peersock, 2);
+        exit(err ? 1 : 0);
+    }
+  exit:
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Socket select loop.
+ * Accepts connections on the tcp socket.
+ *
+ * @param listen_sock tcp listen socket
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_select(Args *args, int listen_sock){
+    int err = 0;
+    SelectSet set = {};
+    dprintf("> socks: %d\n", listen_sock);
+    while(1){
+        SelectSet_zero(&set);
+        SelectSet_add_read(&set, listen_sock);
+        err = SelectSet_select(&set, NULL);
+        if(err < 0){
+            if(errno == EINTR) continue;
+            perror("select");
+            goto exit;
+        }
+        if(FD_ISSET(listen_sock, &set.rd)){
+            xfrd_accept(args, listen_sock);
+        }
+    }
+  exit:
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Create a socket.
+ *
+ * @param args program arguments
+ * @param socktype socket type
+ * @param reuse whether to set SO_REUSEADDR
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int create_socket(Args *args, int socktype, int reuse, int *val){
+    int err = 0;
+    int sock = 0;
+    struct sockaddr_in addr_in;
+    struct sockaddr *addr = (struct sockaddr *)&addr_in;
+    socklen_t addr_n = sizeof(addr_in);
+
+    dprintf(">\n");
+    // Create socket and bind it.
+    sock = socket(AF_INET, socktype, 0);
+    if(sock < 0){
+        err = -errno;
+        goto exit;
+    }
+    addr_in.sin_family = AF_INET;
+    addr_in.sin_addr.s_addr = INADDR_ANY;
+    addr_in.sin_port = args->port;
+    dprintf("> port=%d\n", ntohs(addr_in.sin_port));
+    if(reuse){
+        // Set socket option to reuse address.
+        int val = 1;
+        err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+        if(err < 0){
+            err = -errno;
+            perror("setsockopt");
+            goto exit;
+        }
+    }
+    err = bind(sock, addr, addr_n);
+    if(err < 0){
+        err = -errno;
+        perror("bind");
+        goto exit;
+    }
+  exit:
+    *val = (err ? -1 : sock);
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Create the tcp listen socket.
+ *
+ * @param args program arguments
+ * @param val return value for the socket
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_listen_socket(Args *args, int *val){
+    int err = 0;
+    int sock;
+    dprintf(">\n");
+    err = create_socket(args, SOCK_STREAM, 1, &sock);
+    if(err) goto exit;
+    dprintf("> listen...\n");
+    err = listen(sock, 5);
+    if(err < 0){
+        err = -errno;
+        perror("listen");
+        goto exit;
+    }
+  exit:
+    *val = (err ? -1 : sock);
+    if(err) close(sock);
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Type for signal handling functions. */
+typedef void SignalAction(int code, siginfo_t *info, void *data);
+
+/** Handle SIGCHLD by getting child exit status.
+ * This prevents child processes being defunct.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+void sigaction_SIGCHLD(int code, siginfo_t *info, void *data){
+    int status;
+    pid_t pid;
+    //dprintf("> child_exit=%d waiting...\n", child_exit);
+    pid = wait(&status);
+    dprintf("> child pid=%d status=%d\n", pid, status);
+}
+
+/** Handle SIGPIPE.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+void sigaction_SIGPIPE(int code, siginfo_t *info, void *data){
+    dprintf("> SIGPIPE\n");
+    //fflush(stdout);
+    //fflush(stderr);
+    //exit(1);
+}
+
+/** Handle SIGALRM.
+ *
+ * @param code signal code
+ * @param info signal info
+ * @param data
+ */
+void sigaction_SIGALRM(int code, siginfo_t *info, void *data){
+    dprintf("> SIGALRM\n");
+}
+
+/** Install a handler for a signal.
+ *
+ * @param signum signal
+ * @param action handler
+ * @return 0 on success, error code otherwise
+ */
+int catch_signal(int signum, SignalAction *action){
+    int err = 0;
+    struct sigaction sig = {};
+    sig.sa_sigaction = action;
+    sig.sa_flags = SA_SIGINFO;
+    err = sigaction(signum, &sig, NULL);
+    if(err){
+        perror("sigaction");
+    }
+    return err;
+}    
+
+/** Transfer daemon main program.
+ *
+ * @param args program arguments
+ * @return 0 on success, error code otherwise
+ */
+int xfrd_main(Args *args){
+    int err = 0;
+    int listen_sock;
+
+    dprintf(">\n");
+    catch_signal(SIGCHLD,sigaction_SIGCHLD);
+    catch_signal(SIGPIPE,sigaction_SIGPIPE);
+    catch_signal(SIGALRM,sigaction_SIGALRM); 
+    err  = xfrd_listen_socket(args, &listen_sock);
+    if(err) goto exit;
+    err = xfrd_select(args, listen_sock);
+  exit:
+    close(listen_sock);
+    dprintf("< err=%d\n", err);
+    return err;
+}
+
+/** Parse command-line arguments and call the xfrd main program.
+ *
+ * @param arg argument count
+ * @param argv arguments
+ * @return 0 on success, 1 otherwise
+ */
+int main(int argc, char *argv[]){
+    int err = 0;
+    int key = 0;
+    int long_index = 0;
+
+    set_defaults(args);
+    while(1){
+       key = getopt_long(argc, argv, short_opts, long_opts, &long_index);
+       if(key == -1) break;
+       switch(key){
+        case OPT_PORT:
+            err = !convert_service_to_port(optarg, &args->port);
+            if(err) goto exit;
+            break;
+        case OPT_COMPRESS:
+            args->compress = TRUE;
+            break;
+       case OPT_HELP:
+           usage(0);
+           break;
+       case OPT_VERBOSE:
+           args->verbose = TRUE;
+           break;
+       case OPT_VERSION:
+            printf("> Version %d.%d\n", XFR_PROTO_MAJOR, XFR_PROTO_MINOR);
+            exit(0);
+           break;
+       default:
+           usage(EINVAL);
+           break;
+       }
+    }
+    xfr_init();
+    err = xfrd_main(args);
+  exit:
+    if(err && key > 0){
+        fprintf(stderr, "Error in arg %c\n", key);
+    }
+    return (err ? 1 : 0);
+}
+
diff --git a/tools/xfrd/xfrd.h b/tools/xfrd/xfrd.h
new file mode 100644 (file)
index 0000000..2537fd6
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef _XFRD_XFRD_H_
+#define _XFRD_XFRD_H_
+
+/** Xend port in host order. */
+#define XEND_PORT 8001
+
+/** Xfrd port in host order. */
+#define XFRD_PORT 8002
+
+/** Protocol version. */
+#define XFR_PROTO_MAJOR   1
+#define XFR_PROTO_MINOR   0
+
+#endif
diff --git a/tools/xfrd/xfrdClient.py b/tools/xfrd/xfrdClient.py
new file mode 100755 (executable)
index 0000000..b8a3554
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/env python
+"""
+Test client for the migration daemon (xfrd).
+
+Author: Mike Wray <mike.wray@hp.com>
+
+"""
+import getopt
+import sys
+import os
+from socket import *
+import StringIO
+
+sys.path.append("/home/mjw/repos-bk/xeno-unstable.bk/tools/python")
+
+import xen.xend.sxp as sxp
+from xen.xend.packing import SxpPacker, SxpUnpacker
+
+XFRD_PORT = 8002
+
+verbose = 0
+
+class TCPClient:
+        
+    def __init__(self, host, port):
+        print ">TCPClient"
+        self.sock = socket(AF_INET, SOCK_STREAM, 0)
+        print ">TCPClient sock=", self.sock
+        print ">TCPClient> connect ", host, port
+        v = self.sock.connect((host, port))
+        print ">TCPClient> connect=", v
+        # Send plain header (no gzip).
+        #self.sock.send("\0\0")
+        
+        self.sockin = self.sock.makefile("r")
+        self.sockout = self.sock.makefile("w")
+        self.packer = SxpPacker(self.sockout)
+        self.unpacker = SxpUnpacker(self.sockin)
+        #pass
+
+    def request(self, req):
+        print "request>", req
+        self.packer.pack(req)
+        self.sockout.flush()
+        print "request<"
+
+    def request_hello(self):
+        self.request(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR])
+
+    def request_migrate(self, vmid, vhost, vport, vmconfig='(vm)'):
+        self.request(['xfr.migrate', vmid, vmconfig, vhost, vport])
+
+    def read(self):
+        while(1):
+            v = self.unpacker.unpack()
+            print 'read>', v
+            if v[0] == 'xfr.err' and v[1]: return
+            if v[0] == 'xfr.ok': return
+
+XFR_PROTO_MAJOR = 1
+XFR_PROTO_MINOR = 0
+
+host_default = "localhost"
+port_default = XFRD_PORT
+vhost_default = "localhost"
+vport_default = 8003
+vmid_default = 1
+
+# Short options. Options followed by ':' need a parameter.
+short_opts = 'h'
+
+# Long options. Options ending in '=' need a parameter.
+long_opts = [ 'host=', 'port=', 'vhost=', 'vport=', 'vmid=', 'verbose', 'help']
+
+def usage(err=None):
+    if err:
+        out = sys.stderr
+    else:
+        out = sys.stdout
+    print >> out, 'Usage: %s [options] [command...]\n' % sys.argv[0]
+    print >> out, '--host <host>\n\tHost to initiate transfer on. Default %s.' % host_default
+    print >> out, '--port <port>\n\tPort to initiate transfer on. Default %d.' % port_default 
+    print >> out, '--vhost <vhost>\n\tHost to transfer VM to. Default %s.' % vhost_default
+    print >> out, '--vport <vport>\n\tPort to transfer VM to. Default %d.' % vport_default
+    print >> out, '--vmid <vmid>\n\tVM id. Default %d.' % vmid_default
+    print >> out, '--help\n\tPrint help.'
+
+def main(argv):
+    global verbose
+    host = host_default
+    port = port_default
+    vhost = vhost_default
+    vport = vport_default
+    vmid = vmid_default
+
+    try:
+        opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
+    except getopt.GetoptError, ex:
+        print >>sys.stderr, 'Error:', ex
+        usage(1)
+        sys.exit(1)
+
+    for key, val in opts:
+        if key == '--help':
+            usage()
+            sys.exit(0)
+        elif key == '--host':
+            host = val
+        elif key == '--port':
+            port = int(val)
+        elif key == '--vhost':
+            vhost = val
+        elif key == '--vport':
+            vport = int(val)
+        elif key == '--vmid':
+            vmid = int(val)
+
+    print "host=%s port=%d" % (host, port)
+    print "vhost=%s vport=%d vmid=%d" % (vhost, vport, vmid)
+    client = TCPClient(gethostbyname(host), port)
+    client.request_hello()
+    client.request_migrate(vmid, gethostbyname(vhost), vport)
+    client.read()
+
+if __name__ == '__main__':
+        main(sys.argv)
+